0
0
mirror of https://github.com/vim/vim.git synced 2025-09-28 04:24:06 -04:00

patch 8.2.0499: calling a lambda is slower than evaluating a string

Problem:    Calling a lambda is slower than evaluating a string.
Solution:   Make calling a lambda faster. (Ken Takata, closes #5727)
This commit is contained in:
Bram Moolenaar
2020-04-02 18:34:35 +02:00
parent 4227c789ff
commit f10806b250
2 changed files with 60 additions and 29 deletions

View File

@@ -24,6 +24,7 @@
#define FC_SANDBOX 0x40 // function defined in the sandbox #define FC_SANDBOX 0x40 // function defined in the sandbox
#define FC_DEAD 0x80 // function kept only for reference to dfunc #define FC_DEAD 0x80 // function kept only for reference to dfunc
#define FC_EXPORT 0x100 // "export def Func()" #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. * All user-defined functions are found in this hashtable.
@@ -384,6 +385,9 @@ get_lambda_tv(char_u **arg, typval_T *rettv, int evaluate)
((char_u **)(newlines.ga_data))[newlines.ga_len++] = p; ((char_u **)(newlines.ga_data))[newlines.ga_len++] = p;
STRCPY(p, "return "); STRCPY(p, "return ");
vim_strncpy(p + 7, s, e - s); 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; fp->uf_refcount = 1;
set_ufunc_name(fp, name); set_ufunc_name(fp, name);
@@ -1106,15 +1110,18 @@ call_user_func(
} }
/* /*
* Init a: variables. * Init a: variables, unless none found (in lambda).
* Set a:0 to "argcount" less number of named arguments, if >= 0. * Set a:0 to "argcount" less number of named arguments, if >= 0.
* Set a:000 to a list with room for the "..." arguments. * Set a:000 to a list with room for the "..." arguments.
*/ */
init_var_dict(&fc->l_avars, &fc->l_avars_var, VAR_SCOPE); 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", add_nr_var(&fc->l_avars, &fc->fixvar[fixvar_idx++].var, "0",
(varnumber_T)(argcount >= fp->uf_args.ga_len (varnumber_T)(argcount >= fp->uf_args.ga_len
? argcount - fp->uf_args.ga_len : 0)); ? argcount - fp->uf_args.ga_len : 0));
fc->l_avars.dv_lock = VAR_FIXED; 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 // Use "name" to avoid a warning from some compiler that checks the
// destination size. // destination size.
v = &fc->fixvar[fixvar_idx++].var; v = &fc->fixvar[fixvar_idx++].var;
@@ -1125,6 +1132,7 @@ call_user_func(
v->di_tv.v_type = VAR_LIST; v->di_tv.v_type = VAR_LIST;
v->di_tv.v_lock = VAR_FIXED; v->di_tv.v_lock = VAR_FIXED;
v->di_tv.vval.v_list = &fc->l_varlist; v->di_tv.vval.v_list = &fc->l_varlist;
}
vim_memset(&fc->l_varlist, 0, sizeof(list_T)); vim_memset(&fc->l_varlist, 0, sizeof(list_T));
fc->l_varlist.lv_refcount = DO_NOT_FREE_CNT; fc->l_varlist.lv_refcount = DO_NOT_FREE_CNT;
fc->l_varlist.lv_lock = VAR_FIXED; fc->l_varlist.lv_lock = VAR_FIXED;
@@ -1133,11 +1141,15 @@ call_user_func(
* Set a:firstline to "firstline" and a:lastline to "lastline". * Set a:firstline to "firstline" and a:lastline to "lastline".
* Set a:name to named arguments. * Set a:name to named arguments.
* Set a:N to the "..." 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", add_nr_var(&fc->l_avars, &fc->fixvar[fixvar_idx++].var, "firstline",
(varnumber_T)firstline); (varnumber_T)firstline);
add_nr_var(&fc->l_avars, &fc->fixvar[fixvar_idx++].var, "lastline", add_nr_var(&fc->l_avars, &fc->fixvar[fixvar_idx++].var, "lastline",
(varnumber_T)lastline); (varnumber_T)lastline);
}
for (i = 0; i < argcount || i < fp->uf_args.ga_len; ++i) for (i = 0; i < argcount || i < fp->uf_args.ga_len; ++i)
{ {
int addlocal = FALSE; int addlocal = FALSE;
@@ -1173,6 +1185,10 @@ call_user_func(
} }
else else
{ {
if ((fp->uf_flags & FC_NOARGS) != 0)
// Bail out if no a: arguments used (in lambda).
break;
// "..." argument a:1, a:2, etc. // "..." argument a:1, a:2, etc.
sprintf((char *)numbuf, "%d", ai + 1); sprintf((char *)numbuf, "%d", ai + 1);
name = numbuf; name = numbuf;
@@ -1298,6 +1314,16 @@ call_user_func(
if (default_arg_err && (fp->uf_flags & FC_ABORT)) if (default_arg_err && (fp->uf_flags & FC_ABORT))
did_emsg = TRUE; 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 else
// call do_cmdline() to execute the lines // call do_cmdline() to execute the lines
do_cmdline(NULL, get_func_line, (void *)fc, do_cmdline(NULL, get_func_line, (void *)fc,
@@ -1734,11 +1760,11 @@ call_func(
int ret = FAIL; int ret = FAIL;
int error = FCERR_NONE; int error = FCERR_NONE;
int i; int i;
ufunc_T *fp; ufunc_T *fp = NULL;
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 = NULL;
char_u *name; char_u *name = NULL;
int argcount = argcount_in; int argcount = argcount_in;
typval_T *argvars = argvars_in; typval_T *argvars = argvars_in;
dict_T *selfdict = funcexe->selfdict; dict_T *selfdict = funcexe->selfdict;
@@ -1752,13 +1778,18 @@ call_func(
// even when call_func() returns FAIL. // even when call_func() returns FAIL.
rettv->v_type = VAR_UNKNOWN; rettv->v_type = VAR_UNKNOWN;
// Make a copy of the name, if it comes from a funcref variable it could if (partial != NULL)
// be changed or deleted in the called function. 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); name = len > 0 ? vim_strnsave(funcname, len) : vim_strsave(funcname);
if (name == NULL) if (name == NULL)
return ret; return ret;
fname = fname_trans_sid(name, fname_buf, &tofree, &error); fname = fname_trans_sid(name, fname_buf, &tofree, &error);
}
if (funcexe->doesrange != NULL) if (funcexe->doesrange != NULL)
*funcexe->doesrange = FALSE; *funcexe->doesrange = FALSE;
@@ -1793,21 +1824,19 @@ call_func(
char_u *rfname = fname; char_u *rfname = fname;
// Ignore "g:" before a function name. // Ignore "g:" before a function name.
if (fname[0] == 'g' && fname[1] == ':') if (fp == NULL && fname[0] == 'g' && fname[1] == ':')
rfname = fname + 2; rfname = fname + 2;
rettv->v_type = VAR_NUMBER; // default rettv is number zero rettv->v_type = VAR_NUMBER; // default rettv is number zero
rettv->vval.v_number = 0; rettv->vval.v_number = 0;
error = FCERR_UNKNOWN; error = FCERR_UNKNOWN;
if (!builtin_function(rfname, -1)) if (fp != NULL || !builtin_function(rfname, -1))
{ {
/* /*
* User defined function. * User defined function.
*/ */
if (partial != NULL && partial->pt_func != NULL) if (fp == NULL)
fp = partial->pt_func;
else
fp = find_func(rfname, NULL); fp = find_func(rfname, NULL);
// Trigger FuncUndefined event, may load the function. // Trigger FuncUndefined event, may load the function.
@@ -1887,7 +1916,7 @@ theend:
*/ */
if (!aborting()) if (!aborting())
{ {
user_func_error(error, name); user_func_error(error, (name != NULL) ? name : funcname);
} }
// clear the copies made from the partial // clear the copies made from the partial

View File

@@ -738,6 +738,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 */
/**/
499,
/**/ /**/
498, 498,
/**/ /**/