vim-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.

437bafe4c8
This commit is contained in:
Michael Ennen 2016-12-16 14:51:49 -07:00
parent 1f715ac1c1
commit 53fad45115
10 changed files with 384 additions and 227 deletions

View File

@ -1207,7 +1207,8 @@ 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})
@ -2014,8 +2015,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}
@ -3622,12 +3625,31 @@ foreground() Move the Vim window to the foreground. Useful when sent from
|remote_foreground()| instead. |remote_foreground()| instead.
{only in the Win32 GUI and console version} {only in the Win32 GUI and 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}])
Return a |Funcref| variable that refers to function {name}. Return a |Funcref| variable that refers to function {name}.
{name} can be a user defined function or an internal function. {name} can be a user defined function or an internal function.
{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}
argument is not allowed. E.g.: >
let FuncWithArg = function(dict.Func, [arg])
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 mans the argument list and/or the dictionary is stored in That mans 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.
@ -6101,6 +6123,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()|.
@ -7922,6 +7945,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
@ -8149,7 +8173,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

@ -31,8 +31,6 @@ typedef struct {
#include "nvim/hashtab.h" #include "nvim/hashtab.h"
// for dict_T // for dict_T
#include "nvim/eval_defs.h" #include "nvim/eval_defs.h"
// for proftime_T
#include "nvim/profile.h"
// for String // for String
#include "nvim/api/private/defs.h" #include "nvim/api/private/defs.h"
// for Map(K, V) // for Map(K, V)
@ -90,7 +88,6 @@ typedef struct {
typedef struct window_S win_T; typedef struct window_S win_T;
typedef struct wininfo_S wininfo_T; typedef struct wininfo_S wininfo_T;
typedef struct frame_S frame_T; typedef struct frame_S frame_T;
typedef int scid_T; /* script ID */
// for struct memline (it needs memfile_T) // for struct memline (it needs memfile_T)
#include "nvim/memline_defs.h" #include "nvim/memline_defs.h"

View File

@ -218,7 +218,7 @@ static int echo_attr = 0; /* attributes used for ":echo" */
#define FC_DELETED 16 // :delfunction used while uf_refcount > 0 #define FC_DELETED 16 // :delfunction used while uf_refcount > 0
#define FC_REMOVED 32 // function redefined while uf_refcount > 0 #define FC_REMOVED 32 // function redefined while uf_refcount > 0
/* The names of packages that once were loaded are remembered. */ // The names of packages that once were loaded are remembered.
static garray_T ga_loaded = {0, 0, sizeof(char_u *), 4, NULL}; static garray_T ga_loaded = {0, 0, sizeof(char_u *), 4, NULL};
// List heads for garbage collection. Although there can be a reference loop // List heads for garbage collection. Although there can be a reference loop
@ -232,37 +232,6 @@ static list_T *first_list = NULL; // list of all lists
#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 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
proftime_T prof_child; // time spent in a child
funccall_T *caller; // calling function or NULL
int fc_refcount;
int fc_copyID; // for garbage collection
garray_T fc_funcs; // list of ufunc_T* which refer this
};
/* /*
* Info used by a ":for" loop. * Info used by a ":for" loop.
*/ */
@ -273,15 +242,6 @@ typedef struct {
list_T *fi_list; /* list being used */ list_T *fi_list; /* list being used */
} forinfo_T; } forinfo_T;
/*
* 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;
/* /*
* enum used by var_flavour() * enum used by var_flavour()
*/ */
@ -4960,6 +4920,15 @@ static int 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 partial_free(partial_T *pt) static void partial_free(partial_T *pt)
{ {
for (int i = 0; i < pt->pt_argc; i++) { for (int i = 0; i < pt->pt_argc; i++) {
@ -4967,8 +4936,12 @@ static void partial_free(partial_T *pt)
} }
xfree(pt->pt_argv); xfree(pt->pt_argv);
dict_unref(pt->pt_dict); dict_unref(pt->pt_dict);
func_unref(pt->pt_name); if (pt->pt_name != NULL) {
xfree(pt->pt_name); func_unref(pt->pt_name);
xfree(pt->pt_name);
} else {
func_ptr_unref(pt->pt_func);
}
xfree(pt); xfree(pt);
} }
@ -5232,12 +5205,12 @@ static bool 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;
} }
@ -6272,7 +6245,7 @@ bool set_ref_in_item(typval_T *tv, int copyID, ht_stack_T **ht_stack,
// A partial does not have a copyID, because it cannot contain itself. // A partial does not have a copyID, because it cannot contain itself.
if (pt != NULL) { if (pt != NULL) {
abort = abort || set_ref_in_func(pt->pt_name, copyID); abort = abort || set_ref_in_func(pt->pt_name, pt->pt_func, copyID);
if (pt->pt_dict != NULL) { if (pt->pt_dict != NULL) {
typval_T dtv; typval_T dtv;
@ -6289,7 +6262,7 @@ bool set_ref_in_item(typval_T *tv, int copyID, ht_stack_T **ht_stack,
break; break;
} }
case VAR_FUNC: case VAR_FUNC:
abort = abort || set_ref_in_func(tv->vval.v_string, copyID); abort = abort || set_ref_in_func(tv->vval.v_string, NULL, copyID);
break; break;
case VAR_UNKNOWN: case VAR_UNKNOWN:
case VAR_SPECIAL: case VAR_SPECIAL:
@ -6307,21 +6280,23 @@ bool set_ref_in_item(typval_T *tv, int copyID, ht_stack_T **ht_stack,
/// "ht_stack" is used to add hashtabs to be marked. Can be NULL. /// "ht_stack" is used to add hashtabs to be marked. Can be NULL.
/// ///
/// @return TRUE if setting references failed somehow. /// @return TRUE if setting references failed somehow.
int set_ref_in_func(char_u *name, int copyID) int 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; int error;
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;
bool abort = false; bool abort = false;
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) {
if (fc->fc_copyID != copyID) { if (fc->fc_copyID != copyID) {
@ -7026,7 +7001,7 @@ static int register_closure(ufunc_T *fp) {
ga_grow(&current_funccal->fc_funcs, 1); ga_grow(&current_funccal->fc_funcs, 1);
((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;
} }
@ -7041,7 +7016,6 @@ static int 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;
@ -7092,6 +7066,8 @@ static int 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;
snprintf((char *)name, sizeof(name), "<lambda>%d", lambda_no++); snprintf((char *)name, sizeof(name), "<lambda>%d", lambda_no++);
@ -7099,6 +7075,11 @@ static int get_lambda_tv(char_u **arg, typval_T *rettv, int evaluate)
if (fp == NULL) { if (fp == NULL) {
goto errret; goto errret;
} }
pt = (partial_T *)xcalloc(1, (unsigned)(sizeof(partial_T)));
if (pt == NULL) {
xfree(fp);
goto errret;
}
ga_init(&newlines, (int)sizeof(char_u *), 1); ga_init(&newlines, (int)sizeof(char_u *), 1);
ga_grow(&newlines, 1); ga_grow(&newlines, 1);
@ -7138,8 +7119,10 @@ static int 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;
return OK; return OK;
@ -7300,6 +7283,8 @@ static char_u *deref_func_name(
{ {
dictitem_T *v; dictitem_T *v;
int cc; int cc;
char_u *s;
if (partialp != NULL) { if (partialp != NULL) {
*partialp = NULL; *partialp = NULL;
} }
@ -7313,8 +7298,9 @@ static char_u *deref_func_name(
*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) {
@ -7327,8 +7313,9 @@ static char_u *deref_func_name(
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;
@ -7421,6 +7408,7 @@ get_func_tv (
#define ERROR_NONE 5 #define ERROR_NONE 5
#define ERROR_OTHER 6 #define ERROR_OTHER 6
#define ERROR_BOTH 7 #define ERROR_BOTH 7
#define ERROR_DELETED 8
#define FLEN_FIXED 40 #define FLEN_FIXED 40
/// In a script change <SID>name() and s:name() to K_SNR 123_name(). /// In a script change <SID>name() and s:name() to K_SNR 123_name().
@ -7552,12 +7540,14 @@ call_func(
error = ERROR_UNKNOWN; error = ERROR_UNKNOWN;
if (!builtin_function(rfname, -1)) { if (!builtin_function(rfname, -1)) {
/* // User defined function.
* User defined function. if (partial != NULL && partial->pt_func != NULL) {
*/ fp = partial->pt_func;
fp = find_func(rfname); } else {
fp = find_func(rfname);
}
/* Trigger FuncUndefined event, may load the function. */ // Trigger FuncUndefined event, may load the function.
if (fp == NULL if (fp == NULL
&& apply_autocmds(EVENT_FUNCUNDEFINED, rfname, rfname, TRUE, NULL) && apply_autocmds(EVENT_FUNCUNDEFINED, rfname, rfname, TRUE, NULL)
&& !aborting()) { && !aborting()) {
@ -7570,7 +7560,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);
} }
@ -7629,6 +7621,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(e_toomanyarg, name); emsg_funcname(e_toomanyarg, name);
break; break;
@ -8465,7 +8460,7 @@ static void f_call(typval_T *argvars, typval_T *rettv, FunPtr fptr)
func = argvars[0].vval.v_string; func = argvars[0].vval.v_string;
} 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]);
} }
@ -9725,7 +9720,7 @@ static int filter_map_one(typval_T *tv, typval_T *expr, int map, int *remp)
} else if (expr->v_type == VAR_PARTIAL) { } else if (expr->v_type == VAR_PARTIAL) {
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;
@ -10002,15 +9997,14 @@ static void f_foreground(typval_T *argvars, typval_T *rettv, FunPtr fptr)
{ {
} }
/* static void common_function(typval_T *argvars, typval_T *rettv,
* "function()" function bool is_funcref, FunPtr fptr)
*/
static void f_function(typval_T *argvars, typval_T *rettv, FunPtr fptr)
{ {
char_u *s; char_u *s;
char_u *name; char_u *name;
bool use_string = false; bool 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) {
// function(MyFunc, [arg], dict) // function(MyFunc, [arg], dict)
@ -10019,17 +10013,28 @@ static void f_function(typval_T *argvars, typval_T *rettv, FunPtr fptr)
&& argvars[0].vval.v_partial != NULL) { && argvars[0].vval.v_partial != NULL) {
// 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 {
// function('MyFunc', [arg], dict) // function('MyFunc', [arg], dict)
s = get_tv_string(&argvars[0]); s = get_tv_string(&argvars[0]);
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 != NULL) {
s = NULL;
}
}
if (s == NULL || *s == NUL || (use_string && ascii_isdigit(*s))) { if (s == NULL || *s == NUL || (use_string && ascii_isdigit(*s))) {
EMSG2(_(e_invarg2), s); EMSG2(_(e_invarg2), s);
} else if (use_string && vim_strchr(s, AUTOLOAD_CHAR) == NULL } else if (trans_name != NULL
&& !function_exists(s, true)) { && (is_funcref ? find_func(trans_name) == NULL
: !translated_function_exists(trans_name))) {
// Don't check an autoload name for existence here. // Don't check an autoload name for existence here.
EMSG2(_("E700: Unknown function: %s"), s); EMSG2(_("E700: Unknown function: %s"), s);
} else { } else {
@ -10071,7 +10076,7 @@ static void f_function(typval_T *argvars, typval_T *rettv, FunPtr fptr)
if (argvars[dict_idx].v_type != VAR_DICT) { if (argvars[dict_idx].v_type != VAR_DICT) {
EMSG(_("E922: expected a dict")); EMSG(_("E922: expected a dict"));
xfree(name); xfree(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;
@ -10082,7 +10087,7 @@ static void f_function(typval_T *argvars, typval_T *rettv, FunPtr fptr)
EMSG(_("E923: Second argument of function() must be " EMSG(_("E923: Second argument of function() must be "
"a list or a dict")); "a list or a dict"));
xfree(name); xfree(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) {
@ -10090,7 +10095,7 @@ static void f_function(typval_T *argvars, typval_T *rettv, FunPtr fptr)
} }
} }
} }
if (dict_idx > 0 || arg_idx > 0 || arg_pt != NULL) { if (dict_idx > 0 || arg_idx > 0 || arg_pt != NULL || is_funcref) {
partial_T *const pt = xcalloc(1, sizeof(*pt)); partial_T *const pt = xcalloc(1, sizeof(*pt));
// result is a VAR_PARTIAL // result is a VAR_PARTIAL
@ -10103,18 +10108,17 @@ static void f_function(typval_T *argvars, typval_T *rettv, FunPtr fptr)
if (pt->pt_argv == NULL) { if (pt->pt_argv == NULL) {
xfree(pt); xfree(pt);
xfree(name); xfree(name);
return; goto theend;
} else { }
int i = 0; int i = 0;
for (; i < arg_len; i++) { for (; i < arg_len; i++) {
copy_tv(&arg_pt->pt_argv[i], &pt->pt_argv[i]); copy_tv(&arg_pt->pt_argv[i], &pt->pt_argv[i]);
} }
if (lv_len > 0) { if (lv_len > 0) {
for (listitem_T *li = list->lv_first; for (listitem_T *li = list->lv_first;
li != NULL; li != NULL;
li = li->li_next) { li = li->li_next) {
copy_tv(&li->li_tv, &pt->pt_argv[i++]); copy_tv(&li->li_tv, &pt->pt_argv[i++]);
}
} }
} }
} }
@ -10136,8 +10140,18 @@ static void f_function(typval_T *argvars, typval_T *rettv, FunPtr fptr)
} }
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);
xfree(name);
} else if (is_funcref) {
pt->pt_func = find_func(trans_name);
func_ptr_ref(pt->pt_func);
xfree(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;
@ -10148,6 +10162,18 @@ static void f_function(typval_T *argvars, typval_T *rettv, FunPtr fptr)
func_ref(name); func_ref(name);
} }
} }
theend:
xfree(trans_name);
}
static void f_funcref(typval_T *argvars, typval_T *rettv, FunPtr fptr)
{
common_function(argvars, rettv, true, fptr);
}
static void f_function(typval_T *argvars, typval_T *rettv, FunPtr fptr)
{
common_function(argvars, rettv, false, fptr);
} }
/// "garbagecollect()" function /// "garbagecollect()" function
@ -10201,11 +10227,18 @@ static void f_get(typval_T *argvars, typval_T *rettv, FunPtr fptr)
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);
rettv->vval.v_string = vim_strsave(pt->pt_name); if (n == NULL) {
rettv->vval.v_string = NULL;
} else {
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) {
rettv->v_type = VAR_DICT; rettv->v_type = VAR_DICT;
@ -16243,8 +16276,9 @@ static int item_compare2(const void *s1, const void *s2, bool keep_zero)
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.
copy_tv(&si1->item->li_tv, &argv[0]); copy_tv(&si1->item->li_tv, &argv[0]);
@ -17746,7 +17780,7 @@ static bool callback_call(Callback *callback, int argcount_in,
case kCallbackPartial: case kCallbackPartial:
partial = callback->data.partial; partial = callback->data.partial;
name = partial->pt_name; name = partial_name(partial);
break; break;
case kCallbackNone: case kCallbackNone:
@ -19346,7 +19380,7 @@ handle_subscript(
// Invoke the function. Recursive! // Invoke the function. Recursive!
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;
} }
@ -19406,19 +19440,22 @@ static void set_selfdict(typval_T *rettv, dict_T *selfdict)
&& rettv->vval.v_partial->pt_dict != NULL) { && rettv->vval.v_partial->pt_dict != NULL) {
return; return;
} }
char_u *fname = rettv->v_type == VAR_FUNC || rettv->v_type == VAR_STRING char_u *fname;
? rettv->vval.v_string
: 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;
// 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;
} else {
fp = find_func(fname); fname = rettv->v_type == VAR_FUNC ? rettv->vval.v_string
xfree(tofree); : 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);
xfree(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)) {
@ -19439,8 +19476,13 @@ static void set_selfdict(typval_T *rettv, dict_T *selfdict)
// 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) {
size_t arg_size = sizeof(typval_T) * ret_pt->pt_argc; size_t arg_size = sizeof(typval_T) * ret_pt->pt_argc;
pt->pt_argv = (typval_T *)xmalloc(arg_size); pt->pt_argv = (typval_T *)xmalloc(arg_size);
@ -21286,11 +21328,19 @@ void 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)); // This function is referenced somewhere, don't redefine it but
ga_clear_strings(&(fp->uf_lines)); // create a new one.
xfree(name); (fp->uf_refcount)--;
name = NULL; fp = NULL;
overwrite = true;
} else {
// redefine existing function
ga_clear_strings(&(fp->uf_args));
ga_clear_strings(&(fp->uf_lines))
xfree(name);
name = NULL;
}
} }
} else { } else {
char numbuf[20]; char numbuf[20];
@ -21366,12 +21416,15 @@ void 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) {
xfree(fp); hi = hash_find(&func_hashtab, name);
goto erret; hi->hi_key = UF2HIKEY(fp);
} else if (hash_add(&func_hashtab, UF2HIKEY(fp)) == FAIL) {
xfree(fp);
goto erret;
} }
}
fp->uf_refcount = 1; 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) {
@ -21413,8 +21466,8 @@ ret_free:
/// Advances "pp" to just after the function name (if no error). /// Advances "pp" to just after the function name (if no error).
/// ///
/// @return the function name in allocated memory, or NULL for failure. /// @return the function name in allocated memory, or NULL for failure.
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 */
int flags, int flags,
@ -21479,7 +21532,7 @@ trans_function_name (
fdp->fd_di = lv.ll_di; fdp->fd_di = lv.ll_di;
} }
if (lv.ll_tv->v_type == VAR_FUNC && lv.ll_tv->vval.v_string != NULL) { if (lv.ll_tv->v_type == VAR_FUNC && lv.ll_tv->vval.v_string != NULL) {
name = vim_strsave(lv.ll_tv->vval.v_string); name = vim_strsave(partial_name(lv.ll_tv->vval.v_partial));
*pp = end; *pp = end;
} 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) {
@ -21676,11 +21729,9 @@ static void list_func_head(ufunc_T *fp, int indent)
last_set_msg(fp->uf_script_ID); last_set_msg(fp->uf_script_ID);
} }
/* /// 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. ufunc_T *find_func(char_u *name)
*/
static ufunc_T *find_func(char_u *name)
{ {
hashitem_T *hi; hashitem_T *hi;
@ -22098,8 +22149,34 @@ void ex_delfunction(exarg_T *eap)
/* Delete the dict item that refers to the function, it will /* Delete the dict item that refers to the function, it will
* invoke func_unref() and possibly delete the function. */ * invoke func_unref() and possibly delete the function. */
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);
}
}
}
}
/// 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);
} }
} }
@ -22108,9 +22185,7 @@ void ex_delfunction(exarg_T *eap)
*/ */
static void func_free(ufunc_T *fp) static void 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));
xfree(fp->uf_tml_count); xfree(fp->uf_tml_count);
@ -22123,12 +22198,13 @@ static void func_free(ufunc_T *fp)
func_remove(fp); func_remove(fp);
} }
funccal_unref(fp->uf_scoped, fp); funccal_unref(fp->uf_scoped, fp);
func_remove(fp);
xfree(fp); xfree(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 func_unref(char_u *name) void func_unref(char_u *name)
{ {
@ -22154,6 +22230,10 @@ void func_unref(char_u *name)
// fail silently, when lambda function isn't found // fail silently, when lambda function isn't found
fp = find_func(name); fp = find_func(name);
} }
if (fp == NULL && isdigit(*name)) {
EMSG2(_(e_intern2), "func_unref()");
}
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
// when "uf_calls" becomes zero. // when "uf_calls" becomes zero.
@ -22163,10 +22243,12 @@ void func_unref(char_u *name)
} }
} }
static void user_func_unref(ufunc_T *fp) /// Unreference a Function: decrement the reference count and free it when it
/// becomes zero.
void func_ptr_unref(ufunc_T *fp)
{ {
if (--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
// when "uf_calls" becomes zero. // when "uf_calls" becomes zero.
if (fp->uf_calls == 0) { if (fp->uf_calls == 0) {
func_free(fp); func_free(fp);
@ -22174,36 +22256,35 @@ static void user_func_unref(ufunc_T *fp)
} }
} }
/* /// Count a reference to a Function.
* Count a reference to a Function.
*/
void func_ref(char_u *name) void func_ref(char_u *name)
{ {
ufunc_T *fp; ufunc_T *fp;
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)) {
fp = find_func(name); // Only give an error for a numbered function.
if (fp == NULL) { // Fail silently, when named or lambda function isn't found.
EMSG2(_(e_intern2), "func_ref()"); EMSG2(_(e_intern2), "func_ref()");
} else {
(fp->uf_refcount)++;
}
} else if (STRNCMP(name, "<lambda>", 8) == 0) {
// fail silently, when lambda function isn't found.
fp = find_func(name);
if (fp != NULL) {
(fp->uf_refcount)++;
}
} }
} }
/* /// Count a reference to a Function.
* Call a user function. void func_ptr_ref(ufunc_T *fp)
*/ {
if (fp != NULL) {
(fp->uf_refcount)++;
}
}
/// Call a user function.
static void static void
call_user_func ( call_user_func(
ufunc_T *fp, /* pointer to function */ ufunc_T *fp, /* pointer to function */
int argcount, /* nr of args */ int argcount, /* nr of args */
typval_T *argvars, /* arguments */ typval_T *argvars, /* arguments */
@ -22264,7 +22345,7 @@ call_user_func (
fc->fc_refcount = 0; fc->fc_refcount = 0;
fc->fc_copyID = 0; fc->fc_copyID = 0;
ga_init(&fc->fc_funcs, sizeof(ufunc_T *), 1); ga_init(&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;
@ -22579,9 +22660,7 @@ call_user_func (
copy_tv(&li->li_tv, &li->li_tv); copy_tv(&li->li_tv, &li->li_tv);
} }
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 now. // Function was unreferenced while being used, free it now.
func_free(fp); func_free(fp);
} }
@ -22605,33 +22684,32 @@ static void 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;
} else { }
pfc = &(*pfc)->caller;
} }
} }
if (!freed) { }
func_unref(fc->func->uf_name); if (!freed) {
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++) {
if (((ufunc_T **)(fc->fc_funcs.ga_data))[i] == fp) { if (((ufunc_T **)(fc->fc_funcs.ga_data))[i] == fp) {
((ufunc_T **)(fc->fc_funcs.ga_data))[i] = NULL; ((ufunc_T **)(fc->fc_funcs.ga_data))[i] = NULL;
}
} }
} }
} }
} }
} }
/// Return TRUE if items in "fc" do not have "copyID". That means they are not /// @return true if items in "fc" do not have "copyID". That means they are not
/// referenced from anywhere that is in use. /// referenced from anywhere that is in use.
static int can_free_funccal(funccall_T *fc, int copyID) static int can_free_funccal(funccall_T *fc, int copyID)
{ {
@ -22661,10 +22739,10 @@ free_funccal (
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);
// The a: variables typevals may not have been allocated, only free the // The a: variables typevals may not have been allocated, only free the
// allocated variables. // allocated variables.
@ -22680,7 +22758,7 @@ free_funccal (
} }
} }
func_unref(fc->func->uf_name); func_ptr_unref(fc->func);
xfree(fc); xfree(fc);
} }

View File

@ -12,42 +12,6 @@
// All user-defined functions are found in this hashtable. // All user-defined functions are found in this hashtable.
extern hashtab_T func_hashtab; extern hashtab_T func_hashtab;
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
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
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)
};
// From user function to hashitem and back. // From user function to hashitem and back.
EXTERN ufunc_T dumuf; EXTERN ufunc_T dumuf;
#define UF2HIKEY(fp) ((fp)->uf_name) #define UF2HIKEY(fp) ((fp)->uf_name)
@ -163,9 +127,6 @@ extern const list_T *eval_msgpack_type_lists[LAST_MSGPACK_TYPE + 1];
typedef int (*ArgvFunc)(int current_argcount, typval_T *argv, typedef int (*ArgvFunc)(int current_argcount, typval_T *argv,
int called_func_argcount); int called_func_argcount);
/// Maximum number of function arguments
#define MAX_FUNC_ARGS 20
#ifdef INCLUDE_GENERATED_DECLARATIONS #ifdef INCLUDE_GENERATED_DECLARATIONS
# include "eval.h.generated.h" # include "eval.h.generated.h"
#endif #endif

View File

@ -103,6 +103,7 @@ return {
foldtext={}, foldtext={},
foldtextresult={args=1}, foldtextresult={args=1},
foreground={}, foreground={},
funcref={args={1, 3}},
['function']={args={1, 3}}, ['function']={args={1, 3}},
garbagecollect={args={0, 1}}, garbagecollect={args={0, 1}},
get={args={2, 3}}, get={args={2, 3}},

View File

@ -6,6 +6,10 @@
#include "nvim/hashtab.h" #include "nvim/hashtab.h"
#include "nvim/lib/queue.h" #include "nvim/lib/queue.h"
#include "nvim/garray.h" // For garray_T
// for proftime_T
#include "nvim/profile.h"
#include "nvim/pos.h" // for linenr_T
typedef int varnumber_T; typedef int varnumber_T;
typedef double float_T; typedef double float_T;
@ -151,9 +155,86 @@ struct dictvar_S {
QUEUE watchers; ///< Dictionary key watchers set by user code. QUEUE watchers; ///< Dictionary key watchers set by user code.
}; };
typedef int scid_T; // script ID
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
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
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)
};
/// Maximum number of function arguments
#define MAX_FUNC_ARGS 20
#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
proftime_T prof_child; // time spent in a child
funccall_T *caller; // calling function or NULL
int fc_refcount;
int fc_copyID; // for garbage collection
garray_T fc_funcs; // list of ufunc_T* which refer this
};
// structure 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;
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->name.
ufunc_T *pt_func; ///< Function pointer; when NULL lookup function
///< with pt_name.
bool pt_auto; ///< when true the partial was created for using bool 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

@ -6602,7 +6602,7 @@ static int vim_regsub_both(char_u *source, typval_T *expr, char_u *dest,
} else if (expr->v_type == VAR_PARTIAL) { } else if (expr->v_type == VAR_PARTIAL) {
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, 1, argv, call_func(s, (int)STRLEN(s), &rettv, 1, argv,
fill_submatch_list, 0L, 0L, &dummy, fill_submatch_list, 0L, 0L, &dummy,
true, partial, NULL); true, partial, NULL);

View File

@ -150,3 +150,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

@ -259,10 +259,10 @@ endfunction
func Test_closure_refcount() func Test_closure_refcount()
let g:Count = LambdaFoo() let g:Count = LambdaFoo()
call test_garbagecollect_now() call garbagecollect()
call assert_equal(1, g:Count()) call assert_equal(1, g:Count())
let g:Count2 = LambdaFoo() let g:Count2 = LambdaFoo()
call test_garbagecollect_now() call garbagecollect()
call assert_equal(1, g:Count2()) call assert_equal(1, g:Count2())
call assert_equal(2, g:Count()) call assert_equal(2, g:Count())
call assert_equal(3, g:Count2()) call assert_equal(3, g:Count2())

View File

@ -303,7 +303,7 @@ static int included_patches[] = {
// 2140 NA // 2140 NA
// 2139, // 2139,
// 2138 NA // 2138 NA
// 2137, 2137,
2136, 2136,
// 2135, // 2135,
2134, 2134,