From 7cb38d5ac41efc1dce80a2e40c9c55f8a3802d61 Mon Sep 17 00:00:00 2001 From: erw7 Date: Sun, 8 Sep 2019 11:04:37 +0900 Subject: vim-patch:8.1.1563: crash when using closures Problem: Crash when using closures. Solution: Set reference in varlist of funccal when running the garbage collector. (Ozaki Kiichi, closes vim/vim#4554, closes vim/vim#4547) https://github.com/vim/vim/commit/6e5000d493b4f385f901eb97f3ce0c8088373403 --- src/nvim/eval/userfunc.c | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) (limited to 'src/nvim/eval/userfunc.c') diff --git a/src/nvim/eval/userfunc.c b/src/nvim/eval/userfunc.c index 1b80b22213..d5a19dd7b9 100644 --- a/src/nvim/eval/userfunc.c +++ b/src/nvim/eval/userfunc.c @@ -3347,9 +3347,13 @@ bool set_ref_in_previous_funccal(int copyID) { bool abort = false; - for (funccall_T *fc = previous_funccal; fc != NULL; fc = fc->caller) { - abort = abort || set_ref_in_ht(&fc->l_vars.dv_hashtab, copyID + 1, NULL); - abort = abort || set_ref_in_ht(&fc->l_avars.dv_hashtab, copyID + 1, NULL); + for (funccall_T *fc = previous_funccal; !abort && fc != NULL; + fc = fc->caller) { + fc->fc_copyID = copyID + 1; + abort = abort + || set_ref_in_ht(&fc->l_vars.dv_hashtab, copyID + 1, NULL) + || set_ref_in_ht(&fc->l_avars.dv_hashtab, copyID + 1, NULL) + || set_ref_in_list(&fc->l_varlist, copyID + 1, NULL); } return abort; } @@ -3360,9 +3364,11 @@ static bool set_ref_in_funccal(funccall_T *fc, int copyID) if (fc->fc_copyID != copyID) { fc->fc_copyID = copyID; - abort = abort || set_ref_in_ht(&fc->l_vars.dv_hashtab, copyID, NULL); - abort = abort || set_ref_in_ht(&fc->l_avars.dv_hashtab, copyID, NULL); - abort = abort || set_ref_in_func(NULL, fc->func, copyID); + abort = abort + || set_ref_in_ht(&fc->l_vars.dv_hashtab, copyID, NULL) + || set_ref_in_ht(&fc->l_avars.dv_hashtab, copyID, NULL) + || set_ref_in_list(&fc->l_varlist, copyID, NULL) + || set_ref_in_func(NULL, fc->func, copyID); } return abort; } -- cgit From 2a57dce9f2ea157a6f9b15613788cdd8cebc26e5 Mon Sep 17 00:00:00 2001 From: Jan Edmund Lazo Date: Sat, 26 Sep 2020 15:34:17 -0400 Subject: vim-patch:8.1.1319: computing function length name in many places Problem: Computing function length name in many places. Solution: compute name length in call_func(). https://github.com/vim/vim/commit/6ed8819822994512c160006bd1204aa11ae3c494 In call_func(), reassign "len" param to (int)STRLEN(funcname) instead of using vim_strsave() which runs strlen(). "len" param is checked for v:lua functions. call_func() states that strlen() is used if "len" is set to -1. --- src/nvim/eval/userfunc.c | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) (limited to 'src/nvim/eval/userfunc.c') diff --git a/src/nvim/eval/userfunc.c b/src/nvim/eval/userfunc.c index d5a19dd7b9..873fe90797 100644 --- a/src/nvim/eval/userfunc.c +++ b/src/nvim/eval/userfunc.c @@ -367,7 +367,7 @@ void emsg_funcname(char *ermsg, const char_u *name) int get_func_tv( const char_u *name, // name of the function - int len, // length of "name" + int len, // length of "name" or -1 to use strlen() typval_T *rettv, char_u **arg, // argument, pointing to the '(' linenr_T firstline, // first line of range @@ -1291,7 +1291,7 @@ int func_call(char_u *name, typval_T *args, partial_T *partial, tv_copy(TV_LIST_ITEM_TV(item), &argv[argc++]); }); - r = call_func(name, (int)STRLEN(name), rettv, argc, argv, NULL, + r = call_func(name, -1, rettv, argc, argv, NULL, curwin->w_cursor.lnum, curwin->w_cursor.lnum, &dummy, true, partial, selfdict); @@ -1316,7 +1316,7 @@ func_call_skip_call: int call_func( const char_u *funcname, // name of the function - int len, // length of "name" + int len, // length of "name" or -1 to use strlen() typval_T *rettv, // [out] value goes here int argcount_in, // number of "argvars" typval_T *argvars_in, // vars for arguments, must have "argcount" @@ -1350,6 +1350,9 @@ call_func( // Make a copy of the name, if it comes from a funcref variable it could // be changed or deleted in the called function. + if (len <= 0) { + len = (int)STRLEN(funcname); + } name = vim_strnsave(funcname, len); fname = fname_trans_sid(name, fname_buf, &tofree, &error); @@ -2853,7 +2856,7 @@ void ex_call(exarg_T *eap) curwin->w_cursor.coladd = 0; } arg = startarg; - if (get_func_tv(name, (int)STRLEN(name), &rettv, &arg, + if (get_func_tv(name, -1, &rettv, &arg, eap->line1, eap->line2, &doesrange, true, partial, fudi.fd_dict) == FAIL) { failed = true; -- cgit From 578b0b0b52ad036370a6678d1b66e5c61d5bd4e2 Mon Sep 17 00:00:00 2001 From: Jan Edmund Lazo Date: Sat, 26 Sep 2020 19:56:36 -0400 Subject: vim-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 vim/vim#5727) https://github.com/vim/vim/commit/f10806b25090879fdc1a86cc0da2f4f34fd21921 Port "uf_flags" constants from patch 8.2.1054 to sync with Vim. Port user_func_error() from patch 8.2.0149. Port Test_lambda_scope() changes from patch 8.1.0736 so that it passes. --- src/nvim/eval/userfunc.c | 159 +++++++++++++++++++++++++++++------------------ 1 file changed, 98 insertions(+), 61 deletions(-) (limited to 'src/nvim/eval/userfunc.c') diff --git a/src/nvim/eval/userfunc.c b/src/nvim/eval/userfunc.c index 873fe90797..d6418eeb48 100644 --- a/src/nvim/eval/userfunc.c +++ b/src/nvim/eval/userfunc.c @@ -32,7 +32,11 @@ #define FC_DELETED 0x10 // :delfunction used while uf_refcount > 0 #define FC_REMOVED 0x20 // function redefined while uf_refcount > 0 #define FC_SANDBOX 0x40 // function defined in the sandbox -#define FC_CFUNC 0x80 // C function extension +#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 +#define FC_VIM9 0x400 // defined in vim9 script file +#define FC_CFUNC 0x800 // C function extension #ifdef INCLUDE_GENERATED_DECLARATIONS #include "eval/userfunc.c.generated.h" @@ -246,6 +250,10 @@ int get_lambda_tv(char_u **arg, typval_T *rettv, bool evaluate) ((char_u **)(newlines.ga_data))[newlines.ga_len++] = p; STRCPY(p, "return "); STRLCPY(p + 7, s, e - s + 1); + if (strstr((char *)p + 7, "a:") == NULL) { + // No a: variables are used for sure. + flags |= FC_NOARGS; + } fp->uf_refcount = 1; STRCPY(fp->uf_name, name); @@ -853,37 +861,42 @@ void call_user_func(ufunc_T *fp, int argcount, typval_T *argvars, ++selfdict->dv_refcount; } - /* - * Init a: variables. - * Set a:0 to "argcount". - * Set a:000 to a list with room for the "..." arguments. - */ + // Init a: variables, unless none found (in lambda). + // Set a:0 to "argcount". + // 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, (dictitem_T *)&fc->fixvar[fixvar_idx++], "0", - (varnumber_T)(argcount - fp->uf_args.ga_len)); + if ((fp->uf_flags & FC_NOARGS) == 0) { + add_nr_var(&fc->l_avars, (dictitem_T *)&fc->fixvar[fixvar_idx++], "0", + (varnumber_T)(argcount - fp->uf_args.ga_len)); + } fc->l_avars.dv_lock = VAR_FIXED; - // Use "name" to avoid a warning from some compiler that checks the - // destination size. - v = (dictitem_T *)&fc->fixvar[fixvar_idx++]; + if ((fp->uf_flags & FC_NOARGS) == 0) { + // Use "name" to avoid a warning from some compiler that checks the + // destination size. + v = (dictitem_T *)&fc->fixvar[fixvar_idx++]; #ifndef __clang_analyzer__ - name = v->di_key; - STRCPY(name, "000"); + name = v->di_key; + STRCPY(name, "000"); #endif - v->di_flags = DI_FLAGS_RO | DI_FLAGS_FIX; - tv_dict_add(&fc->l_avars, v); - v->di_tv.v_type = VAR_LIST; - v->di_tv.v_lock = VAR_FIXED; - v->di_tv.vval.v_list = &fc->l_varlist; + v->di_flags = DI_FLAGS_RO | DI_FLAGS_FIX; + tv_dict_add(&fc->l_avars, v); + v->di_tv.v_type = VAR_LIST; + v->di_tv.v_lock = VAR_FIXED; + v->di_tv.vval.v_list = &fc->l_varlist; + } tv_list_init_static(&fc->l_varlist); tv_list_set_lock(&fc->l_varlist, VAR_FIXED); // 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, (dictitem_T *)&fc->fixvar[fixvar_idx++], - "firstline", (varnumber_T)firstline); - add_nr_var(&fc->l_avars, (dictitem_T *)&fc->fixvar[fixvar_idx++], - "lastline", (varnumber_T)lastline); + // Skipped when no a: variables used (in lambda). + if ((fp->uf_flags & FC_NOARGS) == 0) { + add_nr_var(&fc->l_avars, (dictitem_T *)&fc->fixvar[fixvar_idx++], + "firstline", (varnumber_T)firstline); + add_nr_var(&fc->l_avars, (dictitem_T *)&fc->fixvar[fixvar_idx++], + "lastline", (varnumber_T)lastline); + } for (int i = 0; i < argcount; i++) { bool addlocal = false; @@ -895,6 +908,10 @@ void call_user_func(ufunc_T *fp, int argcount, typval_T *argvars, addlocal = true; } } else { + if ((fp->uf_flags & FC_NOARGS) != 0) { + // Bail out if no a: arguments used (in lambda). + break; + } // "..." argument a:1, a:2, etc. snprintf((char *)numbuf, sizeof(numbuf), "%d", ai + 1); name = numbuf; @@ -1034,9 +1051,19 @@ void call_user_func(ufunc_T *fp, int argcount, typval_T *argvars, save_did_emsg = did_emsg; did_emsg = FALSE; - // call do_cmdline() to execute the lines - do_cmdline(NULL, get_func_line, (void *)fc, - DOCMD_NOWAIT|DOCMD_VERBOSE|DOCMD_REPEAT); + 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, + DOCMD_NOWAIT|DOCMD_VERBOSE|DOCMD_REPEAT); + } --RedrawingDisabled; @@ -1304,6 +1331,36 @@ func_call_skip_call: return r; } +// Give an error message for the result of a function. +// Nothing if "error" is FCERR_NONE. +static void user_func_error(int error, const char_u *name) + FUNC_ATTR_NONNULL_ALL +{ + switch (error) { + case ERROR_UNKNOWN: + emsg_funcname(N_("E117: Unknown function: %s"), name); + break; + case ERROR_DELETED: + emsg_funcname(N_("E933: Function was deleted: %s"), name); + break; + case ERROR_TOOMANY: + emsg_funcname(_(e_toomanyarg), name); + break; + case ERROR_TOOFEW: + emsg_funcname(N_("E119: Not enough arguments for function: %s"), + name); + break; + case ERROR_SCRIPT: + emsg_funcname(N_("E120: Using not in a script context: %s"), + name); + break; + case ERROR_DICT: + emsg_funcname(N_("E725: Calling dict function without Dictionary: %s"), + name); + break; + } +} + /// Call a function with its resolved parameters /// /// "argv_func", when not NULL, can be used to fill in arguments only when the @@ -1333,11 +1390,11 @@ call_func( { int ret = FAIL; int error = ERROR_NONE; - ufunc_T *fp; + ufunc_T *fp = NULL; char_u fname_buf[FLEN_FIXED + 1]; char_u *tofree = NULL; - char_u *fname; - char_u *name; + char_u *fname = NULL; + char_u *name = NULL; int argcount = argcount_in; typval_T *argvars = argvars_in; dict_T *selfdict = selfdict_in; @@ -1348,14 +1405,18 @@ call_func( // 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. if (len <= 0) { len = (int)STRLEN(funcname); } - name = vim_strnsave(funcname, len); - - fname = fname_trans_sid(name, fname_buf, &tofree, &error); + 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 = vim_strnsave(funcname, len); + fname = fname_trans_sid(name, fname_buf, &tofree, &error); + } *doesrange = false; @@ -1387,7 +1448,7 @@ call_func( char_u *rfname = fname; // Ignore "g:" before a function name. - if (fname[0] == 'g' && fname[1] == ':') { + if (fp == NULL && fname[0] == 'g' && fname[1] == ':') { rfname = fname + 2; } @@ -1400,11 +1461,9 @@ call_func( error = ERROR_NONE; nlua_typval_call((const char *)funcname, len, argvars, argcount, rettv); } - } else if (!builtin_function((const char *)rfname, -1)) { + } else if (fp != NULL || !builtin_function((const char *)rfname, -1)) { // User defined function. - if (partial != NULL && partial->pt_func != NULL) { - fp = partial->pt_func; - } else { + if (fp == NULL) { fp = find_func(rfname); } @@ -1483,29 +1542,7 @@ theend: // Report an error unless the argument evaluation or function call has been // cancelled due to an aborting error, an interrupt, or an exception. if (!aborting()) { - switch (error) { - case ERROR_UNKNOWN: - emsg_funcname(N_("E117: Unknown function: %s"), name); - break; - case ERROR_DELETED: - emsg_funcname(N_("E933: Function was deleted: %s"), name); - break; - case ERROR_TOOMANY: - emsg_funcname(_(e_toomanyarg), name); - break; - case ERROR_TOOFEW: - emsg_funcname(N_("E119: Not enough arguments for function: %s"), - name); - break; - case ERROR_SCRIPT: - emsg_funcname(N_("E120: Using not in a script context: %s"), - name); - break; - case ERROR_DICT: - emsg_funcname(N_("E725: Calling dict function without Dictionary: %s"), - name); - break; - } + user_func_error(error, (name != NULL) ? name : funcname); } while (argv_clear > 0) { -- cgit From 594a69579f5d21356021650f87416e29179dbbf8 Mon Sep 17 00:00:00 2001 From: Jan Edmund Lazo Date: Sat, 26 Sep 2020 23:16:53 -0400 Subject: vim-patch:8.2.0042: clearing funccal values twice Problem: Clearing funccal values twice. Solution: Remove clearing individual fields. https://github.com/vim/vim/commit/eac7ce01e92f3dee6bbccaf7e88680fe2ce286eb --- src/nvim/eval/userfunc.c | 5 ----- 1 file changed, 5 deletions(-) (limited to 'src/nvim/eval/userfunc.c') diff --git a/src/nvim/eval/userfunc.c b/src/nvim/eval/userfunc.c index d6418eeb48..d6a25435fe 100644 --- a/src/nvim/eval/userfunc.c +++ b/src/nvim/eval/userfunc.c @@ -821,17 +821,12 @@ void call_user_func(ufunc_T *fp, int argcount, typval_T *argvars, current_funccal = fc; fc->func = fp; fc->rettv = rettv; - rettv->vval.v_number = 0; - fc->linenr = 0; - fc->returned = FALSE; fc->level = ex_nesting_level; // Check if this function has a breakpoint. fc->breakpoint = dbg_find_breakpoint(false, fp->uf_name, (linenr_T)0); fc->dbg_tick = debug_tick; // Set up fields for closure. - fc->fc_refcount = 0; - fc->fc_copyID = 0; ga_init(&fc->fc_funcs, sizeof(ufunc_T *), 1); func_ptr_ref(fp); -- cgit From 9bac43b1fbe7f9018503ce7da16d4e626e0daabb Mon Sep 17 00:00:00 2001 From: Jan Edmund Lazo Date: Sun, 27 Sep 2020 00:02:47 -0400 Subject: userfunc: abort early on invalid refs Cherry-pick set_ref_in_call_stack() changes from patch 8.1.1575. --- src/nvim/eval/userfunc.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) (limited to 'src/nvim/eval/userfunc.c') diff --git a/src/nvim/eval/userfunc.c b/src/nvim/eval/userfunc.c index d6a25435fe..e0361048bc 100644 --- a/src/nvim/eval/userfunc.c +++ b/src/nvim/eval/userfunc.c @@ -3413,12 +3413,13 @@ bool set_ref_in_call_stack(int copyID) { bool abort = false; - for (funccall_T *fc = current_funccal; fc != NULL; fc = fc->caller) { + for (funccall_T *fc = current_funccal; !abort && fc != NULL; + fc = fc->caller) { abort = abort || set_ref_in_funccal(fc, copyID); } // Also go through the funccal_stack. - for (funccal_entry_T *entry = funccal_stack; entry != NULL; + for (funccal_entry_T *entry = funccal_stack; !abort && entry != NULL; entry = entry->next) { for (funccall_T *fc = entry->top_funccal; !abort && fc != NULL; fc = fc->caller) { -- cgit