0
0
mirror of https://github.com/vim/vim.git synced 2025-07-26 11:04:33 -04:00

patch 7.4.2137

Problem:    Using function() with a name will find another function when it is
            redefined.
Solution:   Add funcref().  Refer to lambda using a partial.  Fix several
            reference counting issues.
This commit is contained in:
Bram Moolenaar 2016-08-01 15:40:54 +02:00
parent 5801644819
commit 437bafe4c8
16 changed files with 447 additions and 231 deletions

View File

@ -1,4 +1,4 @@
*eval.txt* For Vim version 7.4. Last change: 2016 Jul 29 *eval.txt* For Vim version 7.4. Last change: 2016 Jul 31
VIM REFERENCE MANUAL by Bram Moolenaar VIM REFERENCE MANUAL by Bram Moolenaar
@ -1240,7 +1240,9 @@ function returns: >
:let Bar = Foo(4) :let Bar = Foo(4)
:echo Bar(6) :echo Bar(6)
< 5 < 5
See also |:func-closure|.
See also |:func-closure|. Lambda and closure support can be checked with: >
if has('lambda')
Examples for using a lambda expression with |sort()|, |map()| and |filter()|: > Examples for using a lambda expression with |sort()|, |map()| and |filter()|: >
:echo map([1, 2, 3], {idx, val -> val + 1}) :echo map([1, 2, 3], {idx, val -> val + 1})
@ -2071,8 +2073,10 @@ foldlevel({lnum}) Number fold level at {lnum}
foldtext() String line displayed for closed fold foldtext() String line displayed for closed fold
foldtextresult({lnum}) String text for closed fold at {lnum} foldtextresult({lnum}) String text for closed fold at {lnum}
foreground() Number bring the Vim window to the foreground foreground() Number bring the Vim window to the foreground
function({name} [, {arglist}] [, {dict}]) funcref({name} [, {arglist}] [, {dict}])
Funcref reference to function {name} Funcref reference to function {name}
function({name} [, {arglist}] [, {dict}])
Funcref named reference to function {name}
garbagecollect([{atexit}]) none free memory, breaking cyclic references garbagecollect([{atexit}]) none free memory, breaking cyclic references
get({list}, {idx} [, {def}]) any get item {idx} from {list} or {def} get({list}, {idx} [, {def}]) any get item {idx} from {list} or {def}
get({dict}, {key} [, {def}]) any get item {key} from {dict} or {def} get({dict}, {key} [, {def}]) any get item {key} from {dict} or {def}
@ -3850,6 +3854,15 @@ foreground() Move the Vim window to the foreground. Useful when sent from
{only in the Win32, Athena, Motif and GTK GUI versions and the {only in the Win32, Athena, Motif and GTK GUI versions and the
Win32 console version} Win32 console version}
*funcref()*
funcref({name} [, {arglist}] [, {dict}])
Just like |function()|, but the returned Funcref will lookup
the function by reference, not by name. This matters when the
function {name} is redefined later.
Unlike |function()|, {name} must be an existing user function.
Also for autoloaded functions. {name} cannot be a builtin
function.
*function()* *E700* *E922* *E923* *function()* *E700* *E922* *E923*
function({name} [, {arglist}] [, {dict}]) function({name} [, {arglist}] [, {dict}])
@ -3857,12 +3870,16 @@ function({name} [, {arglist}] [, {dict}])
{name} can be the name of a user defined function or an {name} can be the name of a user defined function or an
internal function. internal function.
{name} can also be a Funcref, also a partial. When it is a {name} can also be a Funcref or a partial. When it is a
partial the dict stored in it will be used and the {dict} partial the dict stored in it will be used and the {dict}
argument is not allowed. E.g.: > argument is not allowed. E.g.: >
let FuncWithArg = function(dict.Func, [arg]) let FuncWithArg = function(dict.Func, [arg])
let Broken = function(dict.Func, [arg], dict) let Broken = function(dict.Func, [arg], dict)
< <
When using the Funcref the function will be found by {name},
also when it was redefined later. Use |funcref()| to keep the
same function.
When {arglist} or {dict} is present this creates a partial. When {arglist} or {dict} is present this creates a partial.
That means the argument list and/or the dictionary is stored in That means the argument list and/or the dictionary is stored in
the Funcref and will be used when the Funcref is called. the Funcref and will be used when the Funcref is called.
@ -6191,6 +6208,7 @@ screenrow() *screenrow()*
The result is a Number, which is the current screen row of the The result is a Number, which is the current screen row of the
cursor. The top line has number one. cursor. The top line has number one.
This function is mainly used for testing. This function is mainly used for testing.
Alternatively you can use |winline()|.
Note: Same restrictions as with |screencol()|. Note: Same restrictions as with |screencol()|.
@ -8039,6 +8057,7 @@ insert_expand Compiled with support for CTRL-X expansion commands in
Insert mode. Insert mode.
jumplist Compiled with |jumplist| support. jumplist Compiled with |jumplist| support.
keymap Compiled with 'keymap' support. keymap Compiled with 'keymap' support.
lambda Compiled with |lambda| support.
langmap Compiled with 'langmap' support. langmap Compiled with 'langmap' support.
libcall Compiled with |libcall()| support. libcall Compiled with |libcall()| support.
linebreak Compiled with 'linebreak', 'breakat', 'showbreak' and linebreak Compiled with 'linebreak', 'breakat', 'showbreak' and
@ -8294,7 +8313,7 @@ See |:verbose-cmd| for more information.
:endf[unction] The end of a function definition. Must be on a line :endf[unction] The end of a function definition. Must be on a line
by its own, without other commands. by its own, without other commands.
*:delf* *:delfunction* *E130* *E131* *:delf* *:delfunction* *E130* *E131* *E933*
:delf[unction] {name} Delete function {name}. :delf[unction] {name} Delete function {name}.
{name} can also be a |Dictionary| entry that is a {name} can also be a |Dictionary| entry that is a
|Funcref|: > |Funcref|: >

View File

@ -1124,15 +1124,18 @@ set_callback(
if (callback != NULL && *callback != NUL) if (callback != NULL && *callback != NUL)
{ {
if (partial != NULL) if (partial != NULL)
*cbp = partial->pt_name; *cbp = partial_name(partial);
else else
{
*cbp = vim_strsave(callback); *cbp = vim_strsave(callback);
func_ref(*cbp);
}
} }
else else
*cbp = NULL; *cbp = NULL;
*pp = partial; *pp = partial;
if (*pp != NULL) if (partial != NULL)
++(*pp)->pt_refcount; ++partial->pt_refcount;
} }
/* /*
@ -1279,7 +1282,10 @@ channel_set_req_callback(
item->cq_callback = callback; item->cq_callback = callback;
} }
else else
{
item->cq_callback = vim_strsave(callback); item->cq_callback = vim_strsave(callback);
func_ref(item->cq_callback);
}
item->cq_seq_nr = id; item->cq_seq_nr = id;
item->cq_prev = head->cq_prev; item->cq_prev = head->cq_prev;
head->cq_prev = item; head->cq_prev = item;
@ -3923,14 +3929,24 @@ free_job_options(jobopt_T *opt)
{ {
if (opt->jo_partial != NULL) if (opt->jo_partial != NULL)
partial_unref(opt->jo_partial); partial_unref(opt->jo_partial);
else if (opt->jo_callback != NULL)
func_unref(opt->jo_callback);
if (opt->jo_out_partial != NULL) if (opt->jo_out_partial != NULL)
partial_unref(opt->jo_out_partial); partial_unref(opt->jo_out_partial);
else if (opt->jo_out_cb != NULL)
func_unref(opt->jo_out_cb);
if (opt->jo_err_partial != NULL) if (opt->jo_err_partial != NULL)
partial_unref(opt->jo_err_partial); partial_unref(opt->jo_err_partial);
else if (opt->jo_err_cb != NULL)
func_unref(opt->jo_err_cb);
if (opt->jo_close_partial != NULL) if (opt->jo_close_partial != NULL)
partial_unref(opt->jo_close_partial); partial_unref(opt->jo_close_partial);
else if (opt->jo_close_cb != NULL)
func_unref(opt->jo_close_cb);
if (opt->jo_exit_partial != NULL) if (opt->jo_exit_partial != NULL)
partial_unref(opt->jo_exit_partial); partial_unref(opt->jo_exit_partial);
else if (opt->jo_exit_cb != NULL)
func_unref(opt->jo_exit_cb);
} }
/* /*
@ -4476,7 +4492,10 @@ job_set_options(job_T *job, jobopt_T *opt)
++job->jv_exit_partial->pt_refcount; ++job->jv_exit_partial->pt_refcount;
} }
else else
{
job->jv_exit_cb = vim_strsave(opt->jo_exit_cb); job->jv_exit_cb = vim_strsave(opt->jo_exit_cb);
func_ref(job->jv_exit_cb);
}
} }
} }
} }

View File

@ -5011,6 +5011,17 @@ get_lit_string_tv(char_u **arg, typval_T *rettv, int evaluate)
return OK; return OK;
} }
/*
* Return the function name of the partial.
*/
char_u *
partial_name(partial_T *pt)
{
if (pt->pt_name != NULL)
return pt->pt_name;
return pt->pt_func->uf_name;
}
static void static void
partial_free(partial_T *pt) partial_free(partial_T *pt)
{ {
@ -5020,8 +5031,13 @@ partial_free(partial_T *pt)
clear_tv(&pt->pt_argv[i]); clear_tv(&pt->pt_argv[i]);
vim_free(pt->pt_argv); vim_free(pt->pt_argv);
dict_unref(pt->pt_dict); dict_unref(pt->pt_dict);
func_unref(pt->pt_name); if (pt->pt_name != NULL)
vim_free(pt->pt_name); {
func_unref(pt->pt_name);
vim_free(pt->pt_name);
}
else
func_ptr_unref(pt->pt_func);
vim_free(pt); vim_free(pt);
} }
@ -5051,11 +5067,11 @@ func_equal(
/* empty and NULL function name considered the same */ /* empty and NULL function name considered the same */
s1 = tv1->v_type == VAR_FUNC ? tv1->vval.v_string s1 = tv1->v_type == VAR_FUNC ? tv1->vval.v_string
: tv1->vval.v_partial->pt_name; : partial_name(tv1->vval.v_partial);
if (s1 != NULL && *s1 == NUL) if (s1 != NULL && *s1 == NUL)
s1 = NULL; s1 = NULL;
s2 = tv2->v_type == VAR_FUNC ? tv2->vval.v_string s2 = tv2->v_type == VAR_FUNC ? tv2->vval.v_string
: tv2->vval.v_partial->pt_name; : partial_name(tv2->vval.v_partial);
if (s2 != NULL && *s2 == NUL) if (s2 != NULL && *s2 == NUL)
s2 = NULL; s2 = NULL;
if (s1 == NULL || s2 == NULL) if (s1 == NULL || s2 == NULL)
@ -5550,7 +5566,7 @@ set_ref_in_item(
} }
else if (tv->v_type == VAR_FUNC) else if (tv->v_type == VAR_FUNC)
{ {
abort = set_ref_in_func(tv->vval.v_string, copyID); abort = set_ref_in_func(tv->vval.v_string, NULL, copyID);
} }
else if (tv->v_type == VAR_PARTIAL) else if (tv->v_type == VAR_PARTIAL)
{ {
@ -5561,7 +5577,7 @@ set_ref_in_item(
*/ */
if (pt != NULL) if (pt != NULL)
{ {
abort = set_ref_in_func(pt->pt_name, copyID); abort = set_ref_in_func(pt->pt_name, pt->pt_func, copyID);
if (pt->pt_dict != NULL) if (pt->pt_dict != NULL)
{ {
@ -5735,7 +5751,7 @@ echo_string_core(
{ {
partial_T *pt = tv->vval.v_partial; partial_T *pt = tv->vval.v_partial;
char_u *fname = string_quote(pt == NULL ? NULL char_u *fname = string_quote(pt == NULL ? NULL
: pt->pt_name, FALSE); : partial_name(pt), FALSE);
garray_T ga; garray_T ga;
int i; int i;
char_u *tf; char_u *tf;
@ -6871,7 +6887,7 @@ handle_subscript(
if (functv.v_type == VAR_PARTIAL) if (functv.v_type == VAR_PARTIAL)
{ {
pt = functv.vval.v_partial; pt = functv.vval.v_partial;
s = pt->pt_name; s = partial_name(pt);
} }
else else
s = functv.vval.v_string; s = functv.vval.v_string;
@ -10025,7 +10041,7 @@ filter_map_one(typval_T *tv, typval_T *expr, int map, int *remp)
{ {
partial_T *partial = expr->vval.v_partial; partial_T *partial = expr->vval.v_partial;
s = partial->pt_name; s = partial_name(partial);
if (call_func(s, (int)STRLEN(s), &rettv, 2, argv, NULL, if (call_func(s, (int)STRLEN(s), &rettv, 2, argv, NULL,
0L, 0L, &dummy, TRUE, partial, NULL) == FAIL) 0L, 0L, &dummy, TRUE, partial, NULL) == FAIL)
goto theend; goto theend;

View File

@ -148,6 +148,7 @@ static void f_foldlevel(typval_T *argvars, typval_T *rettv);
static void f_foldtext(typval_T *argvars, typval_T *rettv); static void f_foldtext(typval_T *argvars, typval_T *rettv);
static void f_foldtextresult(typval_T *argvars, typval_T *rettv); static void f_foldtextresult(typval_T *argvars, typval_T *rettv);
static void f_foreground(typval_T *argvars, typval_T *rettv); static void f_foreground(typval_T *argvars, typval_T *rettv);
static void f_funcref(typval_T *argvars, typval_T *rettv);
static void f_function(typval_T *argvars, typval_T *rettv); static void f_function(typval_T *argvars, typval_T *rettv);
static void f_garbagecollect(typval_T *argvars, typval_T *rettv); static void f_garbagecollect(typval_T *argvars, typval_T *rettv);
static void f_get(typval_T *argvars, typval_T *rettv); static void f_get(typval_T *argvars, typval_T *rettv);
@ -563,6 +564,7 @@ static struct fst
{"foldtext", 0, 0, f_foldtext}, {"foldtext", 0, 0, f_foldtext},
{"foldtextresult", 1, 1, f_foldtextresult}, {"foldtextresult", 1, 1, f_foldtextresult},
{"foreground", 0, 0, f_foreground}, {"foreground", 0, 0, f_foreground},
{"funcref", 1, 3, f_funcref},
{"function", 1, 3, f_function}, {"function", 1, 3, f_function},
{"garbagecollect", 0, 1, f_garbagecollect}, {"garbagecollect", 0, 1, f_garbagecollect},
{"get", 2, 3, f_get}, {"get", 2, 3, f_get},
@ -1723,7 +1725,7 @@ f_call(typval_T *argvars, typval_T *rettv)
else if (argvars[0].v_type == VAR_PARTIAL) else if (argvars[0].v_type == VAR_PARTIAL)
{ {
partial = argvars[0].vval.v_partial; partial = argvars[0].vval.v_partial;
func = partial->pt_name; func = partial_name(partial);
} }
else else
func = get_tv_string(&argvars[0]); func = get_tv_string(&argvars[0]);
@ -3543,16 +3545,14 @@ f_foreground(typval_T *argvars UNUSED, typval_T *rettv UNUSED)
#endif #endif
} }
/*
* "function()" function
*/
static void static void
f_function(typval_T *argvars, typval_T *rettv) common_function(typval_T *argvars, typval_T *rettv, int is_funcref)
{ {
char_u *s; char_u *s;
char_u *name; char_u *name;
int use_string = FALSE; int use_string = FALSE;
partial_T *arg_pt = NULL; partial_T *arg_pt = NULL;
char_u *trans_name = NULL;
if (argvars[0].v_type == VAR_FUNC) if (argvars[0].v_type == VAR_FUNC)
{ {
@ -3564,7 +3564,7 @@ f_function(typval_T *argvars, typval_T *rettv)
{ {
/* function(dict.MyFunc, [arg]) */ /* function(dict.MyFunc, [arg]) */
arg_pt = argvars[0].vval.v_partial; arg_pt = argvars[0].vval.v_partial;
s = arg_pt->pt_name; s = partial_name(arg_pt);
} }
else else
{ {
@ -3573,11 +3573,22 @@ f_function(typval_T *argvars, typval_T *rettv)
use_string = TRUE; use_string = TRUE;
} }
if (((use_string && vim_strchr(s, AUTOLOAD_CHAR) == NULL)
|| is_funcref))
{
name = s;
trans_name = trans_function_name(&name, FALSE,
TFN_INT | TFN_QUIET | TFN_NO_AUTOLOAD | TFN_NO_DEREF, NULL, NULL);
if (*name != NUL)
s = NULL;
}
if (s == NULL || *s == NUL || (use_string && VIM_ISDIGIT(*s))) if (s == NULL || *s == NUL || (use_string && VIM_ISDIGIT(*s)))
EMSG2(_(e_invarg2), s); EMSG2(_(e_invarg2), s);
/* Don't check an autoload name for existence here. */ /* Don't check an autoload name for existence here. */
else if (use_string && vim_strchr(s, AUTOLOAD_CHAR) == NULL else if (trans_name != NULL && (is_funcref
&& !function_exists(s, TRUE)) ? find_func(trans_name) == NULL
: !translated_function_exists(trans_name)))
EMSG2(_("E700: Unknown function: %s"), s); EMSG2(_("E700: Unknown function: %s"), s);
else else
{ {
@ -3625,7 +3636,7 @@ f_function(typval_T *argvars, typval_T *rettv)
{ {
EMSG(_("E922: expected a dict")); EMSG(_("E922: expected a dict"));
vim_free(name); vim_free(name);
return; goto theend;
} }
if (argvars[dict_idx].vval.v_dict == NULL) if (argvars[dict_idx].vval.v_dict == NULL)
dict_idx = 0; dict_idx = 0;
@ -3636,14 +3647,14 @@ f_function(typval_T *argvars, typval_T *rettv)
{ {
EMSG(_("E923: Second argument of function() must be a list or a dict")); EMSG(_("E923: Second argument of function() must be a list or a dict"));
vim_free(name); vim_free(name);
return; goto theend;
} }
list = argvars[arg_idx].vval.v_list; list = argvars[arg_idx].vval.v_list;
if (list == NULL || list->lv_len == 0) if (list == NULL || list->lv_len == 0)
arg_idx = 0; arg_idx = 0;
} }
} }
if (dict_idx > 0 || arg_idx > 0 || arg_pt != NULL) if (dict_idx > 0 || arg_idx > 0 || arg_pt != NULL || is_funcref)
{ {
partial_T *pt = (partial_T *)alloc_clear(sizeof(partial_T)); partial_T *pt = (partial_T *)alloc_clear(sizeof(partial_T));
@ -3670,17 +3681,14 @@ f_function(typval_T *argvars, typval_T *rettv)
{ {
vim_free(pt); vim_free(pt);
vim_free(name); vim_free(name);
return; goto theend;
}
else
{
for (i = 0; i < arg_len; i++)
copy_tv(&arg_pt->pt_argv[i], &pt->pt_argv[i]);
if (lv_len > 0)
for (li = list->lv_first; li != NULL;
li = li->li_next)
copy_tv(&li->li_tv, &pt->pt_argv[i++]);
} }
for (i = 0; i < arg_len; i++)
copy_tv(&arg_pt->pt_argv[i], &pt->pt_argv[i]);
if (lv_len > 0)
for (li = list->lv_first; li != NULL;
li = li->li_next)
copy_tv(&li->li_tv, &pt->pt_argv[i++]);
} }
/* For "function(dict.func, [], dict)" and "func" is a partial /* For "function(dict.func, [], dict)" and "func" is a partial
@ -3702,8 +3710,23 @@ f_function(typval_T *argvars, typval_T *rettv)
} }
pt->pt_refcount = 1; pt->pt_refcount = 1;
pt->pt_name = name; if (arg_pt != NULL && arg_pt->pt_func != NULL)
func_ref(pt->pt_name); {
pt->pt_func = arg_pt->pt_func;
func_ptr_ref(pt->pt_func);
vim_free(name);
}
else if (is_funcref)
{
pt->pt_func = find_func(trans_name);
func_ptr_ref(pt->pt_func);
vim_free(name);
}
else
{
pt->pt_name = name;
func_ref(name);
}
} }
rettv->v_type = VAR_PARTIAL; rettv->v_type = VAR_PARTIAL;
rettv->vval.v_partial = pt; rettv->vval.v_partial = pt;
@ -3716,6 +3739,26 @@ f_function(typval_T *argvars, typval_T *rettv)
func_ref(name); func_ref(name);
} }
} }
theend:
vim_free(trans_name);
}
/*
* "funcref()" function
*/
static void
f_funcref(typval_T *argvars, typval_T *rettv)
{
common_function(argvars, rettv, TRUE);
}
/*
* "function()" function
*/
static void
f_function(typval_T *argvars, typval_T *rettv)
{
common_function(argvars, rettv, FALSE);
} }
/* /*
@ -3781,14 +3824,20 @@ f_get(typval_T *argvars, typval_T *rettv)
if (pt != NULL) if (pt != NULL)
{ {
char_u *what = get_tv_string(&argvars[1]); char_u *what = get_tv_string(&argvars[1]);
char_u *n;
if (STRCMP(what, "func") == 0 || STRCMP(what, "name") == 0) if (STRCMP(what, "func") == 0 || STRCMP(what, "name") == 0)
{ {
rettv->v_type = (*what == 'f' ? VAR_FUNC : VAR_STRING); rettv->v_type = (*what == 'f' ? VAR_FUNC : VAR_STRING);
if (pt->pt_name == NULL) n = partial_name(pt);
if (n == NULL)
rettv->vval.v_string = NULL; rettv->vval.v_string = NULL;
else else
rettv->vval.v_string = vim_strsave(pt->pt_name); {
rettv->vval.v_string = vim_strsave(n);
if (rettv->v_type == VAR_FUNC)
func_ref(rettv->vval.v_string);
}
} }
else if (STRCMP(what, "dict") == 0) else if (STRCMP(what, "dict") == 0)
{ {
@ -10104,7 +10153,7 @@ item_compare2(const void *s1, const void *s2)
if (partial == NULL) if (partial == NULL)
func_name = sortinfo->item_compare_func; func_name = sortinfo->item_compare_func;
else else
func_name = partial->pt_name; func_name = partial_name(partial);
/* Copy the values. This is needed to be able to set v_lock to VAR_FIXED /* Copy the values. This is needed to be able to set v_lock to VAR_FIXED
* in the copy without changing the original list items. */ * in the copy without changing the original list items. */
@ -11863,16 +11912,14 @@ get_callback(typval_T *arg, partial_T **pp)
{ {
*pp = arg->vval.v_partial; *pp = arg->vval.v_partial;
++(*pp)->pt_refcount; ++(*pp)->pt_refcount;
return (*pp)->pt_name; return partial_name(*pp);
} }
*pp = NULL; *pp = NULL;
if (arg->v_type == VAR_FUNC) if (arg->v_type == VAR_FUNC || arg->v_type == VAR_STRING)
{ {
func_ref(arg->vval.v_string); func_ref(arg->vval.v_string);
return arg->vval.v_string; return arg->vval.v_string;
} }
if (arg->v_type == VAR_STRING)
return arg->vval.v_string;
if (arg->v_type == VAR_NUMBER && arg->vval.v_number == 0) if (arg->v_type == VAR_NUMBER && arg->vval.v_number == 0)
return (char_u *)""; return (char_u *)"";
EMSG(_("E921: Invalid callback argument")); EMSG(_("E921: Invalid callback argument"));

View File

@ -3134,7 +3134,7 @@ vim_to_mzscheme_impl(typval_T *vim_value, int depth, Scheme_Hash_Table *visited)
/* FIXME: func_ref() and func_unref() are needed. */ /* FIXME: func_ref() and func_unref() are needed. */
/* TODO: Support pt_dict and pt_argv. */ /* TODO: Support pt_dict and pt_argv. */
funcname = scheme_make_byte_string( funcname = scheme_make_byte_string(
(char *)vim_value->vval.v_partial->pt_name); (char *)partial_name(vim_value->vval.v_partial));
MZ_GC_CHECK(); MZ_GC_CHECK();
result = scheme_make_closed_prim_w_arity(vim_funcref, funcname, result = scheme_make_closed_prim_w_arity(vim_funcref, funcname,
(const char *)BYTE_STRING_VALUE(funcname), 0, -1); (const char *)BYTE_STRING_VALUE(funcname), 0, -1);

View File

@ -6310,7 +6310,7 @@ ConvertToPyObject(typval_T *tv)
if (tv->vval.v_partial->pt_dict != NULL) if (tv->vval.v_partial->pt_dict != NULL)
tv->vval.v_partial->pt_dict->dv_refcount++; tv->vval.v_partial->pt_dict->dv_refcount++;
return NEW_FUNCTION(tv->vval.v_partial == NULL return NEW_FUNCTION(tv->vval.v_partial == NULL
? (char_u *)"" : tv->vval.v_partial->pt_name, ? (char_u *)"" : partial_name(tv->vval.v_partial),
tv->vval.v_partial->pt_argc, argv, tv->vval.v_partial->pt_argc, argv,
tv->vval.v_partial->pt_dict, tv->vval.v_partial->pt_dict,
tv->vval.v_partial->pt_auto); tv->vval.v_partial->pt_auto);

View File

@ -1217,16 +1217,20 @@ free_all_mem(void)
if (delete_first_msg() == FAIL) if (delete_first_msg() == FAIL)
break; break;
# ifdef FEAT_EVAL
eval_clear();
# endif
# ifdef FEAT_JOB_CHANNEL # ifdef FEAT_JOB_CHANNEL
channel_free_all(); channel_free_all();
job_free_all();
# endif # endif
#ifdef FEAT_TIMERS #ifdef FEAT_TIMERS
timer_free_all(); timer_free_all();
#endif #endif
# ifdef FEAT_EVAL
/* must be after channel_free_all() with unrefs partials */
eval_clear();
# endif
# ifdef FEAT_JOB_CHANNEL
/* must be after eval_clear() with unrefs jobs */
job_free_all();
# endif
free_termoptions(); free_termoptions();

View File

@ -40,6 +40,7 @@ char_u *get_user_var_name(expand_T *xp, int idx);
int eval0(char_u *arg, typval_T *rettv, char_u **nextcmd, int evaluate); int eval0(char_u *arg, typval_T *rettv, char_u **nextcmd, int evaluate);
int eval1(char_u **arg, typval_T *rettv, int evaluate); int eval1(char_u **arg, typval_T *rettv, int evaluate);
int get_option_tv(char_u **arg, typval_T *rettv, int evaluate); int get_option_tv(char_u **arg, typval_T *rettv, int evaluate);
char_u *partial_name(partial_T *pt);
void partial_unref(partial_T *pt); void partial_unref(partial_T *pt);
int tv_equal(typval_T *tv1, typval_T *tv2, int ic, int recursive); int tv_equal(typval_T *tv1, typval_T *tv2, int ic, int recursive);
int get_copyID(void); int get_copyID(void);

View File

@ -3,9 +3,11 @@ void func_init(void);
int get_lambda_tv(char_u **arg, typval_T *rettv, int evaluate); int get_lambda_tv(char_u **arg, typval_T *rettv, int evaluate);
char_u *deref_func_name(char_u *name, int *lenp, partial_T **partialp, int no_autoload); char_u *deref_func_name(char_u *name, int *lenp, partial_T **partialp, int no_autoload);
int get_func_tv(char_u *name, int len, typval_T *rettv, char_u **arg, linenr_T firstline, linenr_T lastline, int *doesrange, int evaluate, partial_T *partial, dict_T *selfdict); int get_func_tv(char_u *name, int len, typval_T *rettv, char_u **arg, linenr_T firstline, linenr_T lastline, int *doesrange, int evaluate, partial_T *partial, dict_T *selfdict);
ufunc_T *find_func(char_u *name);
void free_all_functions(void); void free_all_functions(void);
int func_call(char_u *name, typval_T *args, partial_T *partial, dict_T *selfdict, typval_T *rettv); int func_call(char_u *name, typval_T *args, partial_T *partial, dict_T *selfdict, typval_T *rettv);
int call_func(char_u *funcname, int len, typval_T *rettv, int argcount_in, typval_T *argvars_in, int (*argv_func)(int, typval_T *, int), linenr_T firstline, linenr_T lastline, int *doesrange, int evaluate, partial_T *partial, dict_T *selfdict_in); int call_func(char_u *funcname, int len, typval_T *rettv, int argcount_in, typval_T *argvars_in, int (*argv_func)(int, typval_T *, int), linenr_T firstline, linenr_T lastline, int *doesrange, int evaluate, partial_T *partial, dict_T *selfdict_in);
char_u *trans_function_name(char_u **pp, int skip, int flags, funcdict_T *fdp, partial_T **partial);
void ex_function(exarg_T *eap); void ex_function(exarg_T *eap);
int eval_fname_script(char_u *p); int eval_fname_script(char_u *p);
int translated_function_exists(char_u *name); int translated_function_exists(char_u *name);
@ -17,7 +19,9 @@ void prof_child_exit(proftime_T *tm);
char_u *get_user_func_name(expand_T *xp, int idx); char_u *get_user_func_name(expand_T *xp, int idx);
void ex_delfunction(exarg_T *eap); void ex_delfunction(exarg_T *eap);
void func_unref(char_u *name); void func_unref(char_u *name);
void func_ptr_unref(ufunc_T *fp);
void func_ref(char_u *name); void func_ref(char_u *name);
void func_ptr_ref(ufunc_T *fp);
void ex_return(exarg_T *eap); void ex_return(exarg_T *eap);
void ex_call(exarg_T *eap); void ex_call(exarg_T *eap);
int do_return(exarg_T *eap, int reanimate, int is_cmd, void *rettv); int do_return(exarg_T *eap, int reanimate, int is_cmd, void *rettv);
@ -51,5 +55,5 @@ dictitem_T *find_var_in_scoped_ht(char_u *name, char_u **varname, int no_autoloa
int set_ref_in_previous_funccal(int copyID); int set_ref_in_previous_funccal(int copyID);
int set_ref_in_call_stack(int copyID); int set_ref_in_call_stack(int copyID);
int set_ref_in_func_args(int copyID); int set_ref_in_func_args(int copyID);
int set_ref_in_func(char_u *name, int copyID); int set_ref_in_func(char_u *name, ufunc_T *fp_in, int copyID);
/* vim: set ft=c : */ /* vim: set ft=c : */

View File

@ -7499,7 +7499,7 @@ vim_regsub_both(
{ {
partial_T *partial = expr->vval.v_partial; partial_T *partial = expr->vval.v_partial;
s = partial->pt_name; s = partial_name(partial);
call_func(s, (int)STRLEN(s), &rettv, call_func(s, (int)STRLEN(s), &rettv,
1, argv, fill_submatch_list, 1, argv, fill_submatch_list,
0L, 0L, &dummy, TRUE, partial, NULL); 0L, 0L, &dummy, TRUE, partial, NULL);

View File

@ -1295,10 +1295,100 @@ struct dictvar_S
dict_T *dv_used_prev; /* previous dict in used dicts list */ dict_T *dv_used_prev; /* previous dict in used dicts list */
}; };
#if defined(FEAT_EVAL) || defined(PROTO)
typedef struct funccall_S funccall_T;
/*
* Structure to hold info for a user function.
*/
typedef struct
{
int uf_varargs; /* variable nr of arguments */
int uf_flags;
int uf_calls; /* nr of active calls */
garray_T uf_args; /* arguments */
garray_T uf_lines; /* function lines */
#ifdef FEAT_PROFILE
int uf_profiling; /* TRUE when func is being profiled */
/* profiling the function as a whole */
int uf_tm_count; /* nr of calls */
proftime_T uf_tm_total; /* time spent in function + children */
proftime_T uf_tm_self; /* time spent in function itself */
proftime_T uf_tm_children; /* time spent in children this call */
/* profiling the function per line */
int *uf_tml_count; /* nr of times line was executed */
proftime_T *uf_tml_total; /* time spent in a line + children */
proftime_T *uf_tml_self; /* time spent in a line itself */
proftime_T uf_tml_start; /* start time for current line */
proftime_T uf_tml_children; /* time spent in children for this line */
proftime_T uf_tml_wait; /* start wait time for current line */
int uf_tml_idx; /* index of line being timed; -1 if none */
int uf_tml_execed; /* line being timed was executed */
#endif
scid_T uf_script_ID; /* ID of script where function was defined,
used for s: variables */
int uf_refcount; /* for numbered function: reference count */
funccall_T *uf_scoped; /* l: local variables for closure */
char_u uf_name[1]; /* name of function (actually longer); can
start with <SNR>123_ (<SNR> is K_SPECIAL
KS_EXTRA KE_SNR) */
} ufunc_T;
#define MAX_FUNC_ARGS 20 /* maximum number of function arguments */
#define VAR_SHORT_LEN 20 /* short variable name length */
#define FIXVAR_CNT 12 /* number of fixed variables */
/* structure to hold info for a function that is currently being executed. */
struct funccall_S
{
ufunc_T *func; /* function being called */
int linenr; /* next line to be executed */
int returned; /* ":return" used */
struct /* fixed variables for arguments */
{
dictitem_T var; /* variable (without room for name) */
char_u room[VAR_SHORT_LEN]; /* room for the name */
} fixvar[FIXVAR_CNT];
dict_T l_vars; /* l: local function variables */
dictitem_T l_vars_var; /* variable for l: scope */
dict_T l_avars; /* a: argument variables */
dictitem_T l_avars_var; /* variable for a: scope */
list_T l_varlist; /* list for a:000 */
listitem_T l_listitems[MAX_FUNC_ARGS]; /* listitems for a:000 */
typval_T *rettv; /* return value */
linenr_T breakpoint; /* next line with breakpoint or zero */
int dbg_tick; /* debug_tick when breakpoint was set */
int level; /* top nesting level of executed function */
#ifdef FEAT_PROFILE
proftime_T prof_child; /* time spent in a child */
#endif
funccall_T *caller; /* calling function or NULL */
/* for closure */
int fc_refcount;
int fc_copyID; /* for garbage collection */
garray_T fc_funcs; /* list of ufunc_T* which refer this */
};
/*
* Struct used by trans_function_name()
*/
typedef struct
{
dict_T *fd_dict; /* Dictionary used */
char_u *fd_newkey; /* new key in "dict" in allocated memory */
dictitem_T *fd_di; /* Dictionary item used */
} funcdict_T;
#endif
struct partial_S struct partial_S
{ {
int pt_refcount; /* reference count */ int pt_refcount; /* reference count */
char_u *pt_name; /* function name */ char_u *pt_name; /* function name; when NULL use
* pt_func->uf_name */
ufunc_T *pt_func; /* function pointer; when NULL lookup function
* with pt_name */
int pt_auto; /* when TRUE the partial was created for using int pt_auto; /* when TRUE the partial was created for using
dict.member in handle_subscript() */ dict.member in handle_subscript() */
int pt_argc; /* number of arguments */ int pt_argc; /* number of arguments */

View File

@ -179,3 +179,18 @@ func Test_function_with_funcref()
call assert_equal(v:t_string, s:fref('x')) call assert_equal(v:t_string, s:fref('x'))
call assert_fails("call function('s:f')", 'E700:') call assert_fails("call function('s:f')", 'E700:')
endfunc endfunc
func Test_funcref()
func! One()
return 1
endfunc
let OneByName = function('One')
let OneByRef = funcref('One')
func! One()
return 2
endfunc
call assert_equal(2, OneByName())
call assert_equal(1, OneByRef())
let OneByRef = funcref('One')
call assert_equal(2, OneByRef())
endfunc

View File

@ -152,7 +152,7 @@ function! Test_lambda_delfunc()
endfunction endfunction
let l:F = s:gen() let l:F = s:gen()
call assert_fails(':call l:F()', 'E117:') call assert_fails(':call l:F()', 'E933:')
endfunction endfunction
function! Test_lambda_scope() function! Test_lambda_scope()

View File

@ -14,52 +14,12 @@
#include "vim.h" #include "vim.h"
#if defined(FEAT_EVAL) || defined(PROTO) #if defined(FEAT_EVAL) || defined(PROTO)
typedef struct funccall_S funccall_T;
/*
* Structure to hold info for a user function.
*/
typedef struct ufunc ufunc_T;
struct ufunc
{
int uf_varargs; /* variable nr of arguments */
int uf_flags;
int uf_calls; /* nr of active calls */
garray_T uf_args; /* arguments */
garray_T uf_lines; /* function lines */
#ifdef FEAT_PROFILE
int uf_profiling; /* TRUE when func is being profiled */
/* profiling the function as a whole */
int uf_tm_count; /* nr of calls */
proftime_T uf_tm_total; /* time spent in function + children */
proftime_T uf_tm_self; /* time spent in function itself */
proftime_T uf_tm_children; /* time spent in children this call */
/* profiling the function per line */
int *uf_tml_count; /* nr of times line was executed */
proftime_T *uf_tml_total; /* time spent in a line + children */
proftime_T *uf_tml_self; /* time spent in a line itself */
proftime_T uf_tml_start; /* start time for current line */
proftime_T uf_tml_children; /* time spent in children for this line */
proftime_T uf_tml_wait; /* start wait time for current line */
int uf_tml_idx; /* index of line being timed; -1 if none */
int uf_tml_execed; /* line being timed was executed */
#endif
scid_T uf_script_ID; /* ID of script where function was defined,
used for s: variables */
int uf_refcount; /* for numbered function: reference count */
funccall_T *uf_scoped; /* l: local variables for closure */
char_u uf_name[1]; /* name of function (actually longer); can
start with <SNR>123_ (<SNR> is K_SPECIAL
KS_EXTRA KE_SNR) */
};
/* function flags */ /* function flags */
#define FC_ABORT 1 /* abort function on error */ #define FC_ABORT 1 /* abort function on error */
#define FC_RANGE 2 /* function accepts range */ #define FC_RANGE 2 /* function accepts range */
#define FC_DICT 4 /* Dict function, uses "self" */ #define FC_DICT 4 /* Dict function, uses "self" */
#define FC_CLOSURE 8 /* closure, uses outer scope variables */ #define FC_CLOSURE 8 /* closure, uses outer scope variables */
#define FC_DELETED 16 /* :delfunction used while uf_refcount > 0 */
/* From user function to hashitem and back. */ /* From user function to hashitem and back. */
#define UF2HIKEY(fp) ((fp)->uf_name) #define UF2HIKEY(fp) ((fp)->uf_name)
@ -69,52 +29,6 @@ struct ufunc
#define FUNCARG(fp, j) ((char_u **)(fp->uf_args.ga_data))[j] #define FUNCARG(fp, j) ((char_u **)(fp->uf_args.ga_data))[j]
#define FUNCLINE(fp, j) ((char_u **)(fp->uf_lines.ga_data))[j] #define FUNCLINE(fp, j) ((char_u **)(fp->uf_lines.ga_data))[j]
#define MAX_FUNC_ARGS 20 /* maximum number of function arguments */
#define VAR_SHORT_LEN 20 /* short variable name length */
#define FIXVAR_CNT 12 /* number of fixed variables */
/* structure to hold info for a function that is currently being executed. */
struct funccall_S
{
ufunc_T *func; /* function being called */
int linenr; /* next line to be executed */
int returned; /* ":return" used */
struct /* fixed variables for arguments */
{
dictitem_T var; /* variable (without room for name) */
char_u room[VAR_SHORT_LEN]; /* room for the name */
} fixvar[FIXVAR_CNT];
dict_T l_vars; /* l: local function variables */
dictitem_T l_vars_var; /* variable for l: scope */
dict_T l_avars; /* a: argument variables */
dictitem_T l_avars_var; /* variable for a: scope */
list_T l_varlist; /* list for a:000 */
listitem_T l_listitems[MAX_FUNC_ARGS]; /* listitems for a:000 */
typval_T *rettv; /* return value */
linenr_T breakpoint; /* next line with breakpoint or zero */
int dbg_tick; /* debug_tick when breakpoint was set */
int level; /* top nesting level of executed function */
#ifdef FEAT_PROFILE
proftime_T prof_child; /* time spent in a child */
#endif
funccall_T *caller; /* calling function or NULL */
/* for closure */
int fc_refcount;
int fc_copyID; /* for garbage collection */
garray_T fc_funcs; /* list of ufunc_T* which refer this */
};
/*
* Struct used by trans_function_name()
*/
typedef struct
{
dict_T *fd_dict; /* Dictionary used */
char_u *fd_newkey; /* new key in "dict" in allocated memory */
dictitem_T *fd_di; /* Dictionary item used */
} funcdict_T;
/* /*
* All user-defined functions are found in this hashtable. * All user-defined functions are found in this hashtable.
*/ */
@ -271,7 +185,7 @@ register_closure(ufunc_T *fp)
return FAIL; return FAIL;
((ufunc_T **)current_funccal->fc_funcs.ga_data) ((ufunc_T **)current_funccal->fc_funcs.ga_data)
[current_funccal->fc_funcs.ga_len++] = fp; [current_funccal->fc_funcs.ga_len++] = fp;
func_ref(current_funccal->func->uf_name); func_ptr_ref(current_funccal->func);
return OK; return OK;
} }
@ -288,7 +202,6 @@ get_lambda_tv(char_u **arg, typval_T *rettv, int evaluate)
ufunc_T *fp = NULL; ufunc_T *fp = NULL;
int varargs; int varargs;
int ret; int ret;
char_u name[20];
char_u *start = skipwhite(*arg + 1); char_u *start = skipwhite(*arg + 1);
char_u *s, *e; char_u *s, *e;
static int lambda_no = 0; static int lambda_no = 0;
@ -331,14 +244,22 @@ get_lambda_tv(char_u **arg, typval_T *rettv, int evaluate)
if (evaluate) if (evaluate)
{ {
int len, flags = 0; int len, flags = 0;
char_u *p; char_u *p;
char_u name[20];
partial_T *pt;
sprintf((char*)name, "<lambda>%d", ++lambda_no); sprintf((char*)name, "<lambda>%d", ++lambda_no);
fp = (ufunc_T *)alloc_clear((unsigned)(sizeof(ufunc_T) + STRLEN(name))); fp = (ufunc_T *)alloc_clear((unsigned)(sizeof(ufunc_T) + STRLEN(name)));
if (fp == NULL) if (fp == NULL)
goto errret; goto errret;
pt = (partial_T *)alloc_clear((unsigned)sizeof(partial_T));
if (pt == NULL)
{
vim_free(fp);
goto errret;
}
ga_init2(&newlines, (int)sizeof(char_u *), 1); ga_init2(&newlines, (int)sizeof(char_u *), 1);
if (ga_grow(&newlines, 1) == FAIL) if (ga_grow(&newlines, 1) == FAIL)
@ -380,8 +301,10 @@ get_lambda_tv(char_u **arg, typval_T *rettv, int evaluate)
fp->uf_calls = 0; fp->uf_calls = 0;
fp->uf_script_ID = current_SID; fp->uf_script_ID = current_SID;
rettv->vval.v_string = vim_strsave(name); pt->pt_func = fp;
rettv->v_type = VAR_FUNC; pt->pt_refcount = 1;
rettv->vval.v_partial = pt;
rettv->v_type = VAR_PARTIAL;
} }
eval_lavars_used = old_eval_lavars; eval_lavars_used = old_eval_lavars;
@ -406,6 +329,7 @@ deref_func_name(char_u *name, int *lenp, partial_T **partialp, int no_autoload)
{ {
dictitem_T *v; dictitem_T *v;
int cc; int cc;
char_u *s;
if (partialp != NULL) if (partialp != NULL)
*partialp = NULL; *partialp = NULL;
@ -421,8 +345,9 @@ deref_func_name(char_u *name, int *lenp, partial_T **partialp, int no_autoload)
*lenp = 0; *lenp = 0;
return (char_u *)""; /* just in case */ return (char_u *)""; /* just in case */
} }
*lenp = (int)STRLEN(v->di_tv.vval.v_string); s = v->di_tv.vval.v_string;
return v->di_tv.vval.v_string; *lenp = (int)STRLEN(s);
return s;
} }
if (v != NULL && v->di_tv.v_type == VAR_PARTIAL) if (v != NULL && v->di_tv.v_type == VAR_PARTIAL)
@ -436,8 +361,9 @@ deref_func_name(char_u *name, int *lenp, partial_T **partialp, int no_autoload)
} }
if (partialp != NULL) if (partialp != NULL)
*partialp = pt; *partialp = pt;
*lenp = (int)STRLEN(pt->pt_name); s = partial_name(pt);
return pt->pt_name; *lenp = (int)STRLEN(s);
return s;
} }
return name; return name;
@ -611,7 +537,7 @@ fname_trans_sid(char_u *name, char_u *fname_buf, char_u **tofree, int *error)
* Find a function by name, return pointer to it in ufuncs. * Find a function by name, return pointer to it in ufuncs.
* Return NULL for unknown function. * Return NULL for unknown function.
*/ */
static ufunc_T * ufunc_T *
find_func(char_u *name) find_func(char_u *name)
{ {
hashitem_T *hi; hashitem_T *hi;
@ -678,7 +604,7 @@ free_funccal(
* funccall_T, don't clear it then. */ * funccall_T, don't clear it then. */
if (fp->uf_scoped == fc) if (fp->uf_scoped == fc)
fp->uf_scoped = NULL; fp->uf_scoped = NULL;
func_unref(fc->func->uf_name); func_ptr_unref(fc->func);
} }
} }
ga_clear(&fc->fc_funcs); ga_clear(&fc->fc_funcs);
@ -695,7 +621,7 @@ free_funccal(
for (li = fc->l_varlist.lv_first; li != NULL; li = li->li_next) for (li = fc->l_varlist.lv_first; li != NULL; li = li->li_next)
clear_tv(&li->li_tv); clear_tv(&li->li_tv);
func_unref(fc->func->uf_name); func_ptr_unref(fc->func);
vim_free(fc); vim_free(fc);
} }
@ -759,7 +685,7 @@ call_user_func(
fc->fc_refcount = 0; fc->fc_refcount = 0;
fc->fc_copyID = 0; fc->fc_copyID = 0;
ga_init2(&fc->fc_funcs, sizeof(ufunc_T *), 1); ga_init2(&fc->fc_funcs, sizeof(ufunc_T *), 1);
func_ref(fp->uf_name); func_ptr_ref(fp);
if (STRNCMP(fp->uf_name, "<lambda>", 8) == 0) if (STRNCMP(fp->uf_name, "<lambda>", 8) == 0)
islambda = TRUE; islambda = TRUE;
@ -1112,24 +1038,25 @@ funccal_unref(funccall_T *fc, ufunc_T *fp)
if (--fc->fc_refcount <= 0) if (--fc->fc_refcount <= 0)
{ {
for (pfc = &previous_funccal; *pfc != NULL; ) for (pfc = &previous_funccal; *pfc != NULL; pfc = &(*pfc)->caller)
{ {
if (fc == *pfc if (fc == *pfc)
&& fc->l_varlist.lv_refcount == DO_NOT_FREE_CNT {
if (fc->l_varlist.lv_refcount == DO_NOT_FREE_CNT
&& fc->l_vars.dv_refcount == DO_NOT_FREE_CNT && fc->l_vars.dv_refcount == DO_NOT_FREE_CNT
&& fc->l_avars.dv_refcount == DO_NOT_FREE_CNT) && fc->l_avars.dv_refcount == DO_NOT_FREE_CNT)
{ {
*pfc = fc->caller; *pfc = fc->caller;
free_funccal(fc, TRUE); free_funccal(fc, TRUE);
freed = TRUE; freed = TRUE;
}
break;
} }
else
pfc = &(*pfc)->caller;
} }
} }
if (!freed) if (!freed)
{ {
func_unref(fc->func->uf_name); func_ptr_unref(fc->func);
if (fp != NULL) if (fp != NULL)
for (i = 0; i < fc->fc_funcs.ga_len; ++i) for (i = 0; i < fc->fc_funcs.ga_len; ++i)
@ -1140,14 +1067,25 @@ funccal_unref(funccall_T *fc, ufunc_T *fp)
} }
} }
/*
* Remove the function from the function hashtable. If the function was
* deleted while it still has references this was already done.
*/
static void
func_remove(ufunc_T *fp)
{
hashitem_T *hi = hash_find(&func_hashtab, UF2HIKEY(fp));
if (!HASHITEM_EMPTY(hi))
hash_remove(&func_hashtab, hi);
}
/* /*
* Free a function and remove it from the list of functions. * Free a function and remove it from the list of functions.
*/ */
static void static void
func_free(ufunc_T *fp) func_free(ufunc_T *fp)
{ {
hashitem_T *hi;
/* clear this function */ /* clear this function */
ga_clear_strings(&(fp->uf_args)); ga_clear_strings(&(fp->uf_args));
ga_clear_strings(&(fp->uf_lines)); ga_clear_strings(&(fp->uf_lines));
@ -1156,13 +1094,7 @@ func_free(ufunc_T *fp)
vim_free(fp->uf_tml_total); vim_free(fp->uf_tml_total);
vim_free(fp->uf_tml_self); vim_free(fp->uf_tml_self);
#endif #endif
func_remove(fp);
/* remove the function from the function hashtable */
hi = hash_find(&func_hashtab, UF2HIKEY(fp));
if (HASHITEM_EMPTY(hi))
EMSG2(_(e_intern2), "func_free()");
else
hash_remove(&func_hashtab, hi);
funccal_unref(fp->uf_scoped, fp); funccal_unref(fp->uf_scoped, fp);
@ -1333,7 +1265,10 @@ call_func(
/* /*
* User defined function. * User defined function.
*/ */
fp = find_func(rfname); if (partial != NULL && partial->pt_func != NULL)
fp = partial->pt_func;
else
fp = find_func(rfname);
#ifdef FEAT_AUTOCMD #ifdef FEAT_AUTOCMD
/* Trigger FuncUndefined event, may load the function. */ /* Trigger FuncUndefined event, may load the function. */
@ -1353,7 +1288,9 @@ call_func(
fp = find_func(rfname); fp = find_func(rfname);
} }
if (fp != NULL) if (fp != NULL && (fp->uf_flags & FC_DELETED))
error = ERROR_DELETED;
else if (fp != NULL)
{ {
if (argv_func != NULL) if (argv_func != NULL)
argcount = argv_func(argcount, argvars, fp->uf_args.ga_len); argcount = argv_func(argcount, argvars, fp->uf_args.ga_len);
@ -1387,9 +1324,7 @@ call_func(
call_user_func(fp, argcount, argvars, rettv, call_user_func(fp, argcount, argvars, rettv,
firstline, lastline, firstline, lastline,
(fp->uf_flags & FC_DICT) ? selfdict : NULL); (fp->uf_flags & FC_DICT) ? selfdict : NULL);
if (--fp->uf_calls <= 0 && (isdigit(*fp->uf_name) if (--fp->uf_calls <= 0 && fp->uf_refcount <= 0)
|| STRNCMP(fp->uf_name, "<lambda>", 8) == 0)
&& fp->uf_refcount <= 0)
/* Function was unreferenced while being used, free it /* Function was unreferenced while being used, free it
* now. */ * now. */
func_free(fp); func_free(fp);
@ -1433,6 +1368,9 @@ call_func(
case ERROR_UNKNOWN: case ERROR_UNKNOWN:
emsg_funcname(N_("E117: Unknown function: %s"), name); emsg_funcname(N_("E117: Unknown function: %s"), name);
break; break;
case ERROR_DELETED:
emsg_funcname(N_("E933: Function was deleted: %s"), name);
break;
case ERROR_TOOMANY: case ERROR_TOOMANY:
emsg_funcname((char *)e_toomanyarg, name); emsg_funcname((char *)e_toomanyarg, name);
break; break;
@ -1516,7 +1454,7 @@ list_func_head(ufunc_T *fp, int indent)
* TFN_NO_DEREF: do not dereference a Funcref * TFN_NO_DEREF: do not dereference a Funcref
* Advances "pp" to just after the function name (if no error). * Advances "pp" to just after the function name (if no error).
*/ */
static char_u * char_u *
trans_function_name( trans_function_name(
char_u **pp, char_u **pp,
int skip, /* only find the end, don't evaluate */ int skip, /* only find the end, don't evaluate */
@ -1595,7 +1533,7 @@ trans_function_name(
else if (lv.ll_tv->v_type == VAR_PARTIAL else if (lv.ll_tv->v_type == VAR_PARTIAL
&& lv.ll_tv->vval.v_partial != NULL) && lv.ll_tv->vval.v_partial != NULL)
{ {
name = vim_strsave(lv.ll_tv->vval.v_partial->pt_name); name = vim_strsave(partial_name(lv.ll_tv->vval.v_partial));
*pp = end; *pp = end;
if (partial != NULL) if (partial != NULL)
*partial = lv.ll_tv->vval.v_partial; *partial = lv.ll_tv->vval.v_partial;
@ -1752,6 +1690,7 @@ ex_function(exarg_T *eap)
int varargs = FALSE; int varargs = FALSE;
int flags = 0; int flags = 0;
ufunc_T *fp; ufunc_T *fp;
int overwrite = FALSE;
int indent; int indent;
int nesting; int nesting;
char_u *skip_until = NULL; char_u *skip_until = NULL;
@ -2214,11 +2153,22 @@ ex_function(exarg_T *eap)
name); name);
goto erret; goto erret;
} }
/* redefine existing function */ if (fp->uf_refcount > 1)
ga_clear_strings(&(fp->uf_args)); {
ga_clear_strings(&(fp->uf_lines)); /* This function is referenced somewhere, don't redefine it but
vim_free(name); * create a new one. */
name = NULL; --fp->uf_refcount;
fp = NULL;
overwrite = TRUE;
}
else
{
/* redefine existing function */
ga_clear_strings(&(fp->uf_args));
ga_clear_strings(&(fp->uf_lines));
vim_free(name);
name = NULL;
}
} }
} }
else else
@ -2308,7 +2258,6 @@ ex_function(exarg_T *eap)
fudi.fd_di->di_tv.v_type = VAR_FUNC; fudi.fd_di->di_tv.v_type = VAR_FUNC;
fudi.fd_di->di_tv.v_lock = 0; fudi.fd_di->di_tv.v_lock = 0;
fudi.fd_di->di_tv.vval.v_string = vim_strsave(name); fudi.fd_di->di_tv.vval.v_string = vim_strsave(name);
fp->uf_refcount = 1;
/* behave like "dict" was used */ /* behave like "dict" was used */
flags |= FC_DICT; flags |= FC_DICT;
@ -2316,17 +2265,22 @@ ex_function(exarg_T *eap)
/* insert the new function in the function list */ /* insert the new function in the function list */
STRCPY(fp->uf_name, name); STRCPY(fp->uf_name, name);
if (hash_add(&func_hashtab, UF2HIKEY(fp)) == FAIL) if (overwrite)
{
hi = hash_find(&func_hashtab, name);
hi->hi_key = UF2HIKEY(fp);
}
else if (hash_add(&func_hashtab, UF2HIKEY(fp)) == FAIL)
{ {
vim_free(fp); vim_free(fp);
goto erret; goto erret;
} }
fp->uf_refcount = 1;
} }
fp->uf_args = newargs; fp->uf_args = newargs;
fp->uf_lines = newlines; fp->uf_lines = newlines;
if ((flags & FC_CLOSURE) != 0) if ((flags & FC_CLOSURE) != 0)
{ {
++fp->uf_refcount;
if (register_closure(fp) == FAIL) if (register_closure(fp) == FAIL)
goto erret; goto erret;
} }
@ -2750,13 +2704,30 @@ ex_delfunction(exarg_T *eap)
dictitem_remove(fudi.fd_dict, fudi.fd_di); dictitem_remove(fudi.fd_dict, fudi.fd_di);
} }
else else
func_free(fp); {
/* Normal functions (not numbered functions and lambdas) have a
* refcount of 1 for the entry in the hashtable. When deleting
* them and the refcount is more than one, it should be kept.
* Numbered functions and lambdas snould be kept if the refcount is
* one or more. */
if (fp->uf_refcount > (isdigit(fp->uf_name[0])
|| fp->uf_name[0] == '<') ? 0 : 1)
{
/* Function is still referenced somewhere. Don't free it but
* do remove it from the hashtable. */
func_remove(fp);
fp->uf_flags |= FC_DELETED;
fp->uf_refcount--;
}
else
func_free(fp);
}
} }
} }
/* /*
* Unreference a Function: decrement the reference count and free it when it * Unreference a Function: decrement the reference count and free it when it
* becomes zero. Only for numbered functions. * becomes zero.
*/ */
void void
func_unref(char_u *name) func_unref(char_u *name)
@ -2765,22 +2736,30 @@ func_unref(char_u *name)
if (name == NULL) if (name == NULL)
return; return;
if (isdigit(*name)) fp = find_func(name);
if (fp == NULL && isdigit(*name))
{ {
fp = find_func(name);
if (fp == NULL)
{
#ifdef EXITFREE #ifdef EXITFREE
if (!entered_free_all_mem) if (!entered_free_all_mem)
#endif #endif
EMSG2(_(e_intern2), "func_unref()"); EMSG2(_(e_intern2), "func_unref()");
}
} }
else if (STRNCMP(name, "<lambda>", 8) == 0) if (fp != NULL && --fp->uf_refcount <= 0)
{ {
/* fail silently, when lambda function isn't found. */ /* Only delete it when it's not being used. Otherwise it's done
fp = find_func(name); * when "uf_calls" becomes zero. */
if (fp->uf_calls == 0)
func_free(fp);
} }
}
/*
* Unreference a Function: decrement the reference count and free it when it
* becomes zero.
*/
void
func_ptr_unref(ufunc_T *fp)
{
if (fp != NULL && --fp->uf_refcount <= 0) if (fp != NULL && --fp->uf_refcount <= 0)
{ {
/* Only delete it when it's not being used. Otherwise it's done /* Only delete it when it's not being used. Otherwise it's done
@ -2800,21 +2779,23 @@ func_ref(char_u *name)
if (name == NULL) if (name == NULL)
return; return;
fp = find_func(name);
if (fp != NULL)
++fp->uf_refcount;
else if (isdigit(*name)) else if (isdigit(*name))
{ /* Only give an error for a numbered function.
fp = find_func(name); * Fail silently, when named or lambda function isn't found. */
if (fp == NULL) EMSG2(_(e_intern2), "func_ref()");
EMSG2(_(e_intern2), "func_ref()"); }
else
++fp->uf_refcount; /*
} * Count a reference to a Function.
else if (STRNCMP(name, "<lambda>", 8) == 0) */
{ void
/* fail silently, when lambda function isn't found. */ func_ptr_ref(ufunc_T *fp)
fp = find_func(name); {
if (fp != NULL) if (fp != NULL)
++fp->uf_refcount; ++fp->uf_refcount;
}
} }
/* /*
@ -3298,18 +3279,24 @@ func_has_abort(
dict_T * dict_T *
make_partial(dict_T *selfdict_in, typval_T *rettv) make_partial(dict_T *selfdict_in, typval_T *rettv)
{ {
char_u *fname = rettv->v_type == VAR_FUNC ? rettv->vval.v_string char_u *fname;
: rettv->vval.v_partial->pt_name;
char_u *tofree = NULL; char_u *tofree = NULL;
ufunc_T *fp; ufunc_T *fp;
char_u fname_buf[FLEN_FIXED + 1]; char_u fname_buf[FLEN_FIXED + 1];
int error; int error;
dict_T *selfdict = selfdict_in; dict_T *selfdict = selfdict_in;
/* Translate "s:func" to the stored function name. */ if (rettv->v_type == VAR_PARTIAL && rettv->vval.v_partial->pt_func != NULL)
fname = fname_trans_sid(fname, fname_buf, &tofree, &error); fp = rettv->vval.v_partial->pt_func;
fp = find_func(fname); else
vim_free(tofree); {
fname = rettv->v_type == VAR_FUNC ? rettv->vval.v_string
: rettv->vval.v_partial->pt_name;
/* Translate "s:func" to the stored function name. */
fname = fname_trans_sid(fname, fname_buf, &tofree, &error);
fp = find_func(fname);
vim_free(tofree);
}
if (fp != NULL && (fp->uf_flags & FC_DICT)) if (fp != NULL && (fp->uf_flags & FC_DICT))
{ {
@ -3335,8 +3322,16 @@ make_partial(dict_T *selfdict_in, typval_T *rettv)
/* Partial: copy the function name, use selfdict and copy /* Partial: copy the function name, use selfdict and copy
* args. Can't take over name or args, the partial might * args. Can't take over name or args, the partial might
* be referenced elsewhere. */ * be referenced elsewhere. */
pt->pt_name = vim_strsave(ret_pt->pt_name); if (ret_pt->pt_name != NULL)
func_ref(pt->pt_name); {
pt->pt_name = vim_strsave(ret_pt->pt_name);
func_ref(pt->pt_name);
}
else
{
pt->pt_func = ret_pt->pt_func;
func_ptr_ref(pt->pt_func);
}
if (ret_pt->pt_argc > 0) if (ret_pt->pt_argc > 0)
{ {
pt->pt_argv = (typval_T *)alloc( pt->pt_argv = (typval_T *)alloc(
@ -3703,20 +3698,23 @@ set_ref_in_func_args(int copyID)
* Returns TRUE if setting references failed somehow. * Returns TRUE if setting references failed somehow.
*/ */
int int
set_ref_in_func(char_u *name, int copyID) set_ref_in_func(char_u *name, ufunc_T *fp_in, int copyID)
{ {
ufunc_T *fp; ufunc_T *fp = fp_in;
funccall_T *fc; funccall_T *fc;
int error = ERROR_NONE; int error = ERROR_NONE;
char_u fname_buf[FLEN_FIXED + 1]; char_u fname_buf[FLEN_FIXED + 1];
char_u *tofree = NULL; char_u *tofree = NULL;
char_u *fname; char_u *fname;
if (name == NULL) if (name == NULL && fp_in == NULL)
return FALSE; return FALSE;
fname = fname_trans_sid(name, fname_buf, &tofree, &error); if (fp_in == NULL)
fp = find_func(fname); {
fname = fname_trans_sid(name, fname_buf, &tofree, &error);
fp = find_func(fname);
}
if (fp != NULL) if (fp != NULL)
{ {
for (fc = fp->uf_scoped; fc != NULL; fc = fc->func->uf_scoped) for (fc = fp->uf_scoped; fc != NULL; fc = fc->func->uf_scoped)

View File

@ -763,6 +763,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 */
/**/
2137,
/**/ /**/
2136, 2136,
/**/ /**/

View File

@ -2475,6 +2475,7 @@ int vim_main2(int argc, char **argv);
#define ERROR_DICT 4 #define ERROR_DICT 4
#define ERROR_NONE 5 #define ERROR_NONE 5
#define ERROR_OTHER 6 #define ERROR_OTHER 6
#define ERROR_DELETED 7
/* flags for find_name_end() */ /* flags for find_name_end() */
#define FNE_INCL_BR 1 /* include [] in name */ #define FNE_INCL_BR 1 /* include [] in name */