Patch 8.2.0499
Problem: Calling a lambda is slower than evaluating a string.
Solution: Make calling a lambda faster. (Ken Takata, closes #5727)
Files: src/userfunc.c
*** ../vim-8.2.0498/src/userfunc.c 2020-03-28 21:38:02.128802283 +0100
--- src/userfunc.c 2020-04-02 18:32:54.399903028 +0200
***************
*** 24,29 ****
--- 24,30 ----
#define FC_SANDBOX 0x40 // function defined in the sandbox
#define FC_DEAD 0x80 // function kept only for reference to dfunc
#define FC_EXPORT 0x100 // "export def Func()"
+ #define FC_NOARGS 0x200 // no a: variables in lambda
/*
* All user-defined functions are found in this hashtable.
***************
*** 384,389 ****
--- 385,393 ----
((char_u **)(newlines.ga_data))[newlines.ga_len++] = p;
STRCPY(p, "return ");
vim_strncpy(p + 7, s, e - s);
+ if (strstr((char *)p + 7, "a:") == NULL)
+ // No a: variables are used for sure.
+ flags |= FC_NOARGS;
fp->uf_refcount = 1;
set_ufunc_name(fp, name);
***************
*** 1106,1130 ****
}
/*
! * Init a: variables.
* Set a:0 to "argcount" less number of named arguments, if >= 0.
* Set a:000 to a list with room for the "..." arguments.
*/
init_var_dict(&fc->l_avars, &fc->l_avars_var, VAR_SCOPE);
! add_nr_var(&fc->l_avars, &fc->fixvar[fixvar_idx++].var, "0",
(varnumber_T)(argcount >= fp->uf_args.ga_len
? argcount - fp->uf_args.ga_len : 0));
fc->l_avars.dv_lock = VAR_FIXED;
! // Use "name" to avoid a warning from some compiler that checks the
! // destination size.
! v = &fc->fixvar[fixvar_idx++].var;
! name = v->di_key;
! STRCPY(name, "000");
! v->di_flags = DI_FLAGS_RO | DI_FLAGS_FIX;
! hash_add(&fc->l_avars.dv_hashtab, DI2HIKEY(v));
! v->di_tv.v_type = VAR_LIST;
! v->di_tv.v_lock = VAR_FIXED;
! v->di_tv.vval.v_list = &fc->l_varlist;
vim_memset(&fc->l_varlist, 0, sizeof(list_T));
fc->l_varlist.lv_refcount = DO_NOT_FREE_CNT;
fc->l_varlist.lv_lock = VAR_FIXED;
--- 1110,1138 ----
}
/*
! * Init a: variables, unless none found (in lambda).
* Set a:0 to "argcount" less number of named arguments, if >= 0.
* Set a:000 to a list with room for the "..." arguments.
*/
init_var_dict(&fc->l_avars, &fc->l_avars_var, VAR_SCOPE);
! if ((fp->uf_flags & FC_NOARGS) == 0)
! add_nr_var(&fc->l_avars, &fc->fixvar[fixvar_idx++].var, "0",
(varnumber_T)(argcount >= fp->uf_args.ga_len
? argcount - fp->uf_args.ga_len : 0));
fc->l_avars.dv_lock = VAR_FIXED;
! if ((fp->uf_flags & FC_NOARGS) == 0)
! {
! // Use "name" to avoid a warning from some compiler that checks the
! // destination size.
! v = &fc->fixvar[fixvar_idx++].var;
! name = v->di_key;
! STRCPY(name, "000");
! v->di_flags = DI_FLAGS_RO | DI_FLAGS_FIX;
! hash_add(&fc->l_avars.dv_hashtab, DI2HIKEY(v));
! v->di_tv.v_type = VAR_LIST;
! v->di_tv.v_lock = VAR_FIXED;
! v->di_tv.vval.v_list = &fc->l_varlist;
! }
vim_memset(&fc->l_varlist, 0, sizeof(list_T));
fc->l_varlist.lv_refcount = DO_NOT_FREE_CNT;
fc->l_varlist.lv_lock = VAR_FIXED;
***************
*** 1133,1143 ****
* Set a:firstline to "firstline" and a:lastline to "lastline".
* Set a:name to named arguments.
* Set a:N to the "..." arguments.
*/
! add_nr_var(&fc->l_avars, &fc->fixvar[fixvar_idx++].var, "firstline",
(varnumber_T)firstline);
! add_nr_var(&fc->l_avars, &fc->fixvar[fixvar_idx++].var, "lastline",
(varnumber_T)lastline);
for (i = 0; i < argcount || i < fp->uf_args.ga_len; ++i)
{
int addlocal = FALSE;
--- 1141,1155 ----
* Set a:firstline to "firstline" and a:lastline to "lastline".
* Set a:name to named arguments.
* Set a:N to the "..." arguments.
+ * Skipped when no a: variables used (in lambda).
*/
! if ((fp->uf_flags & FC_NOARGS) == 0)
! {
! add_nr_var(&fc->l_avars, &fc->fixvar[fixvar_idx++].var, "firstline",
(varnumber_T)firstline);
! add_nr_var(&fc->l_avars, &fc->fixvar[fixvar_idx++].var, "lastline",
(varnumber_T)lastline);
+ }
for (i = 0; i < argcount || i < fp->uf_args.ga_len; ++i)
{
int addlocal = FALSE;
***************
*** 1173,1178 ****
--- 1185,1194 ----
}
else
{
+ if ((fp->uf_flags & FC_NOARGS) != 0)
+ // Bail out if no a: arguments used (in lambda).
+ break;
+
// "..." argument a:1, a:2, etc.
sprintf((char *)numbuf, "%d", ai + 1);
name = numbuf;
***************
*** 1298,1303 ****
--- 1314,1329 ----
if (default_arg_err && (fp->uf_flags & FC_ABORT))
did_emsg = TRUE;
+ else if (islambda)
+ {
+ char_u *p = *(char_u **)fp->uf_lines.ga_data + 7;
+
+ // A Lambda always has the command "return {expr}". It is much faster
+ // to evaluate {expr} directly.
+ ++ex_nesting_level;
+ eval1(&p, rettv, TRUE);
+ --ex_nesting_level;
+ }
else
// call do_cmdline() to execute the lines
do_cmdline(NULL, get_func_line, (void *)fc,
***************
*** 1734,1744 ****
int ret = FAIL;
int error = FCERR_NONE;
int i;
! ufunc_T *fp;
char_u fname_buf[FLEN_FIXED + 1];
char_u *tofree = NULL;
! char_u *fname;
! char_u *name;
int argcount = argcount_in;
typval_T *argvars = argvars_in;
dict_T *selfdict = funcexe->selfdict;
--- 1760,1770 ----
int ret = FAIL;
int error = FCERR_NONE;
int i;
! ufunc_T *fp = NULL;
char_u fname_buf[FLEN_FIXED + 1];
char_u *tofree = NULL;
! char_u *fname = NULL;
! char_u *name = NULL;
int argcount = argcount_in;
typval_T *argvars = argvars_in;
dict_T *selfdict = funcexe->selfdict;
***************
*** 1752,1764 ****
// even when call_func() returns FAIL.
rettv->v_type = VAR_UNKNOWN;
! // Make a copy of the name, if it comes from a funcref variable it could
! // be changed or deleted in the called function.
! name = len > 0 ? vim_strnsave(funcname, len) : vim_strsave(funcname);
! if (name == NULL)
! return ret;
! fname = fname_trans_sid(name, fname_buf, &tofree, &error);
if (funcexe->doesrange != NULL)
*funcexe->doesrange = FALSE;
--- 1778,1795 ----
// even when call_func() returns FAIL.
rettv->v_type = VAR_UNKNOWN;
! if (partial != NULL)
! fp = partial->pt_func;
! if (fp == NULL)
! {
! // Make a copy of the name, if it comes from a funcref variable it
! // could be changed or deleted in the called function.
! name = len > 0 ? vim_strnsave(funcname, len) : vim_strsave(funcname);
! if (name == NULL)
! return ret;
! fname = fname_trans_sid(name, fname_buf, &tofree, &error);
! }
if (funcexe->doesrange != NULL)
*funcexe->doesrange = FALSE;
***************
*** 1793,1813 ****
char_u *rfname = fname;
// Ignore "g:" before a function name.
! if (fname[0] == 'g' && fname[1] == ':')
rfname = fname + 2;
rettv->v_type = VAR_NUMBER; // default rettv is number zero
rettv->vval.v_number = 0;
error = FCERR_UNKNOWN;
! if (!builtin_function(rfname, -1))
{
/*
* User defined function.
*/
! if (partial != NULL && partial->pt_func != NULL)
! fp = partial->pt_func;
! else
fp = find_func(rfname, NULL);
// Trigger FuncUndefined event, may load the function.
--- 1824,1842 ----
char_u *rfname = fname;
// Ignore "g:" before a function name.
! if (fp == NULL && fname[0] == 'g' && fname[1] == ':')
rfname = fname + 2;
rettv->v_type = VAR_NUMBER; // default rettv is number zero
rettv->vval.v_number = 0;
error = FCERR_UNKNOWN;
! if (fp != NULL || !builtin_function(rfname, -1))
{
/*
* User defined function.
*/
! if (fp == NULL)
fp = find_func(rfname, NULL);
// Trigger FuncUndefined event, may load the function.
***************
*** 1887,1893 ****
*/
if (!aborting())
{
! user_func_error(error, name);
}
// clear the copies made from the partial
--- 1916,1922 ----
*/
if (!aborting())
{
! user_func_error(error, (name != NULL) ? name : funcname);
}
// clear the copies made from the partial
*** ../vim-8.2.0498/src/version.c 2020-04-02 16:00:01.120265119 +0200
--- src/version.c 2020-04-02 18:33:38.647723434 +0200
***************
*** 740,741 ****
--- 740,743 ----
{ /* Add new patch number below this line */
+ /**/
+ 499,
/**/
--
Veni, Vidi, VW -- I came, I saw, I drove around in a little car.
/// Bram Moolenaar -- Br...@Moolenaar.net --
http://www.Moolenaar.net \\\
/// sponsor Vim, vote for features --
http://www.Vim.org/sponsor/ \\\
\\\ an exciting new programming language --
http://www.Zimbu.org ///
\\\ help me help AIDS victims --
http://ICCF-Holland.org ///