To: vim_dev@googlegroups.com Subject: Patch 8.2.0499 Fcc: outbox From: Bram Moolenaar Mime-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ------------ 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 -- Bram@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 ///