diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/nvim/eval.c | 199 | ||||
-rw-r--r-- | src/nvim/eval/typval_encode.c.h | 2 | ||||
-rw-r--r-- | src/nvim/eval_defs.h | 56 | ||||
-rw-r--r-- | src/nvim/regexp.c | 3 | ||||
-rw-r--r-- | src/nvim/testdir/Makefile | 2 | ||||
-rw-r--r-- | src/nvim/testdir/test_lambda.vim | 51 |
6 files changed, 146 insertions, 167 deletions
diff --git a/src/nvim/eval.c b/src/nvim/eval.c index 2e412781af..ac0e25ff2c 100644 --- a/src/nvim/eval.c +++ b/src/nvim/eval.c @@ -620,9 +620,8 @@ void eval_clear(void) // unreferenced lists and dicts (void)garbage_collect(false); - /* functions */ + // functions free_all_functions(); - hash_clear(&func_hashtab); } #endif @@ -5859,6 +5858,7 @@ bool garbage_collect(bool testing) // referenced through previous_funccal. This must be first, because if // the item is referenced elsewhere the funccal must not be freed. for (funccall_T *fc = previous_funccal; fc != NULL; fc = fc->caller) { + fc->fc_copyID = copyID + 1; ABORTING(set_ref_in_ht)(&fc->l_vars.dv_hashtab, copyID + 1, NULL); ABORTING(set_ref_in_ht)(&fc->l_avars.dv_hashtab, copyID + 1, NULL); } @@ -5934,6 +5934,7 @@ bool garbage_collect(bool testing) // function-local variables for (funccall_T *fc = current_funccal; fc != NULL; fc = fc->caller) { + fc->fc_copyID = copyID; ABORTING(set_ref_in_ht)(&fc->l_vars.dv_hashtab, copyID, NULL); ABORTING(set_ref_in_ht)(&fc->l_avars.dv_hashtab, copyID, NULL); } @@ -5961,8 +5962,8 @@ bool garbage_collect(bool testing) // function call arguments, if v:testing is set. for (int i = 0; i < funcargs.ga_len; i++) { - ABORTING(set_ref_in_item)(((typval_T **)funcargs.ga_data)[i], - copyID, NULL, NULL); + ABORTING(set_ref_in_item)(((typval_T **)funcargs.ga_data)[i], + copyID, NULL, NULL); } // v: vars @@ -6248,7 +6249,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. if (pt != NULL) { - abort = abort || set_ref_in_func(pt->pt_name, pt->pt_func, copyID); + abort = set_ref_in_func(pt->pt_name, pt->pt_func, copyID); if (pt->pt_dict != NULL) { typval_T dtv; @@ -6265,7 +6266,7 @@ bool set_ref_in_item(typval_T *tv, int copyID, ht_stack_T **ht_stack, break; } case VAR_FUNC: - abort = abort || set_ref_in_func(tv->vval.v_string, NULL, copyID); + abort = set_ref_in_func(tv->vval.v_string, NULL, copyID); break; case VAR_UNKNOWN: case VAR_SPECIAL: @@ -6287,48 +6288,19 @@ bool set_ref_in_functions(int copyID) ufunc_T *fp; todo = (int)func_hashtab.ht_used; - for (hi = func_hashtab.ht_array; todo > 0 && !got_int; ++hi) { + for (hi = func_hashtab.ht_array; todo > 0 && !got_int; hi++) { if (!HASHITEM_EMPTY(hi)) { todo--; fp = HI2UF(hi); if (!func_name_refcount(fp->uf_name)) { - abort = abort || set_ref_in_func(NULL, fp, copyID); + abort = abort || set_ref_in_func(NULL, fp, copyID); } } } return abort; } -/// Mark all lists and dicts referenced through function "name" with "copyID". -/// "list_stack" is used to add lists 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. -bool set_ref_in_func(char_u *name, ufunc_T *fp_in, int copyID) -{ - ufunc_T *fp = fp_in; - funccall_T *fc; - int error; - char_u fname_buf[FLEN_FIXED + 1]; - char_u *tofree = NULL; - char_u *fname; - bool abort = false; - if (name == NULL && fp_in == NULL) { - return false; - } - if (fp_in == NULL) { - fname = fname_trans_sid(name, fname_buf, &tofree, &error); - fp = find_func(fname); - } - if (fp != NULL) { - for (fc = fp->uf_scoped; fc != NULL; fc = fc->func->uf_scoped) { - abort = abort || set_ref_in_funccal(fc, copyID); - } - } - xfree(tofree); - return abort; -} /// Mark all lists and dicts referenced in given mark /// @@ -6378,7 +6350,7 @@ static inline bool set_ref_dict(dict_T *dict, int copyID) static bool set_ref_in_funccal(funccall_T *fc, int copyID) { - int abort = false; + bool abort = false; if (fc->fc_copyID != copyID) { fc->fc_copyID = copyID; @@ -6936,7 +6908,7 @@ failret: /// Get function arguments. static int get_function_args(char_u **argp, char_u endchar, garray_T *newargs, - int *varargs, int skip) + int *varargs, bool skip) { bool mustend = false; char_u *arg = *argp; @@ -6979,6 +6951,7 @@ static int get_function_args(char_u **argp, char_u endchar, garray_T *newargs, *p = NUL; arg = vim_strsave(arg); if (arg == NULL) { + *p = c; goto err_ret; } @@ -7025,10 +6998,11 @@ err_ret: } /// Register function "fp" as using "current_funccal" as its scope. -static int register_closure(ufunc_T *fp) { +static void register_closure(ufunc_T *fp) +{ if (fp->uf_scoped == current_funccal) { // no change - return OK; + return; } funccal_unref(fp->uf_scoped, fp, false); fp->uf_scoped = current_funccal; @@ -7036,16 +7010,14 @@ static int register_closure(ufunc_T *fp) { ga_grow(¤t_funccal->fc_funcs, 1); ((ufunc_T **)current_funccal->fc_funcs.ga_data) [current_funccal->fc_funcs.ga_len++] = fp; - return OK; } /// Parse a lambda expression and get a Funcref from "*arg". /// /// @return OK or FAIL. Returns NOTDONE for dict or {expr}. -static int get_lambda_tv(char_u **arg, typval_T *rettv, int evaluate) +static int get_lambda_tv(char_u **arg, typval_T *rettv, bool evaluate) { - garray_T newargs; - garray_T newlines; + garray_T newargs = GA_EMPTY_INIT_VALUE; garray_T *pnewargs; ufunc_T *fp = NULL; int varargs; @@ -7056,10 +7028,6 @@ static int get_lambda_tv(char_u **arg, typval_T *rettv, int evaluate) int *old_eval_lavars = eval_lavars_used; int eval_lavars = false; - // TODO(mike): What lengths should be used here? - ga_init(&newargs, (int)sizeof(char_u *), 80); - ga_init(&newlines, (int)sizeof(char_u *), 80); - // First, check if this is a lambda expression. "->" must exists. ret = get_function_args(&start, '-', NULL, NULL, true); if (ret == FAIL || *start != '>') { @@ -7102,14 +7070,13 @@ static int get_lambda_tv(char_u **arg, typval_T *rettv, int evaluate) char_u *p; char_u name[20]; partial_T *pt; + garray_T newlines; - snprintf((char *)name, sizeof(name), "<lambda>%d", lambda_no++); + lambda_no++; + snprintf((char *)name, sizeof(name), "<lambda>%d", lambda_no); - fp = (ufunc_T *)xcalloc(1, (unsigned)(sizeof(ufunc_T) + STRLEN(name))); - if (fp == NULL) { - goto errret; - } - pt = (partial_T *)xcalloc(1, (unsigned)(sizeof(partial_T))); + fp = (ufunc_T *)xcalloc(1, sizeof(ufunc_T) + STRLEN(name)); + pt = (partial_T *)xcalloc(1, sizeof(partial_T)); if (pt == NULL) { xfree(fp); goto errret; @@ -7121,13 +7088,9 @@ static int get_lambda_tv(char_u **arg, typval_T *rettv, int evaluate) // Add "return " before the expression. len = 7 + e - s + 1; p = (char_u *)xmalloc(len); - if (p == NULL) { - goto errret; - } ((char_u **)(newlines.ga_data))[newlines.ga_len++] = p; STRCPY(p, "return "); - STRNCPY(p + 7, s, e - s); - p[7 + e - s] = NUL; + STRLCPY(p + 7, s, e - s + 1); fp->uf_refcount = 1; STRCPY(fp->uf_name, name); @@ -7158,12 +7121,12 @@ static int get_lambda_tv(char_u **arg, typval_T *rettv, int evaluate) rettv->vval.v_partial = pt; rettv->v_type = VAR_PARTIAL; } + eval_lavars_used = old_eval_lavars; return OK; errret: ga_clear_strings(&newargs); - ga_clear_strings(&newlines); xfree(fp); eval_lavars_used = old_eval_lavars; return FAIL; @@ -7291,9 +7254,6 @@ char_u *get_expr_name(expand_T *xp, int idx) return get_user_var_name(xp, ++intidx); } - - - /// Find internal function in hash functions /// /// @param[in] name Name of the function. @@ -7401,7 +7361,7 @@ get_func_tv ( ret = FAIL; if (ret == OK) { - int i = 0; + int i = 0; if (get_vim_var_nr(VV_TESTING)) { // Prepare for calling garbagecollect_for_testing(), need to know @@ -7413,7 +7373,7 @@ get_func_tv ( ga_grow(&funcargs, 1); ((typval_T **)funcargs.ga_data)[funcargs.ga_len++] = &argvars[i]; } - } + } ret = call_func(name, len, rettv, argcount, argvars, NULL, firstline, lastline, doesrange, evaluate, partial, selfdict); @@ -7490,6 +7450,37 @@ fname_trans_sid(char_u *name, char_u *fname_buf, char_u **tofree, int *error) { return fname; } +/// Mark all lists and dicts referenced through function "name" with "copyID". +/// "list_stack" is used to add lists 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. +bool set_ref_in_func(char_u *name, ufunc_T *fp_in, int copyID) +{ + ufunc_T *fp = fp_in; + funccall_T *fc; + int error = ERROR_NONE; + char_u fname_buf[FLEN_FIXED + 1]; + char_u *tofree = NULL; + char_u *fname; + bool abort = false; + if (name == NULL && fp_in == NULL) { + return false; + } + + if (fp_in == NULL) { + fname = fname_trans_sid(name, fname_buf, &tofree, &error); + fp = find_func(fname); + } + if (fp != NULL) { + for (fc = fp->uf_scoped; fc != NULL; fc = fc->func->uf_scoped) { + abort = abort || set_ref_in_funccal(fc, copyID); + } + } + xfree(tofree); + return abort; +} + /// Call a function with its resolved parameters /// /// "argv_func", when not NULL, can be used to fill in arguments only when the @@ -7612,7 +7603,7 @@ call_func( } else { // Call the user function. call_user_func(fp, argcount, argvars, rettv, firstline, lastline, - (fp->uf_flags & FC_DICT) ? selfdict : NULL); + (fp->uf_flags & FC_DICT) ? selfdict : NULL); error = ERROR_NONE; } } @@ -8927,11 +8918,6 @@ static void f_dictwatcheradd(typval_T *argvars, typval_T *rettv, FunPtr fptr) return; } - if (argvars[2].v_type != VAR_FUNC && argvars[2].v_type != VAR_STRING) { - EMSG2(e_invarg2, "funcref"); - return; - } - char *key_pattern = (char *)get_tv_string_chk(argvars + 1); assert(key_pattern); const size_t key_len = STRLEN(argvars[1].vval.v_string); @@ -8943,6 +8929,7 @@ static void f_dictwatcheradd(typval_T *argvars, typval_T *rettv, FunPtr fptr) Callback callback; if (!callback_from_typval(&callback, &argvars[2])) { + EMSG2(e_invarg2, "funcref"); return; } @@ -9744,7 +9731,6 @@ static int filter_map_one(typval_T *tv, typval_T *expr, int map, int *remp) copy_tv(tv, &vimvars[VV_VAL].vv_tv); argv[0] = vimvars[VV_KEY].vv_tv; argv[1] = vimvars[VV_VAL].vv_tv; - s = expr->vval.v_string; if (expr->v_type == VAR_FUNC) { s = expr->vval.v_string; if (call_func(s, (int)STRLEN(s), &rettv, 2, argv, NULL, @@ -10054,22 +10040,22 @@ static void common_function(typval_T *argvars, typval_T *rettv, use_string = true; } - if (((use_string && vim_strchr(s, AUTOLOAD_CHAR) == NULL) || is_funcref)) { + 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) { + if (*name != NUL) { s = NULL; } } if (s == NULL || *s == NUL || (use_string && ascii_isdigit(*s)) || (is_funcref && trans_name == NULL)) { - EMSG2(_(e_invarg2), s); + EMSG2(_(e_invarg2), use_string ? get_tv_string(&argvars[0]) : s); + // Don't check an autoload name for existence here. } else if (trans_name != NULL && (is_funcref ? find_func(trans_name) == NULL : !translated_function_exists(trans_name))) { - // Don't check an autoload name for existence here. EMSG2(_("E700: Unknown function: %s"), s); } else { int dict_idx = 0; @@ -17733,7 +17719,8 @@ static void f_termopen(typval_T *argvars, typval_T *rettv, FunPtr fptr) } // "test_garbagecollect_now()" function -static void f_test_garbagecollect_now(typval_T *argvars, typval_T *rettv, FunPtr fptr) +static void f_test_garbagecollect_now(typval_T *argvars, + typval_T *rettv, FunPtr fptr) { // This is dangerous, any Lists and Dicts used internally may be freed // while still in use. @@ -18481,7 +18468,8 @@ static bool write_list(FILE *fd, list_T *list, bool binary) } /// Initializes a static list with 10 items. -void init_static_list(staticList10_T *sl) { +void init_static_list(staticList10_T *sl) +{ list_T *l = &sl->sl_list; memset(sl, 0, sizeof(staticList10_T)); @@ -19487,7 +19475,8 @@ static void set_selfdict(typval_T *rettv, dict_T *selfdict) if (rettv->v_type == VAR_PARTIAL && rettv->vval.v_partial->pt_func != NULL) { fp = rettv->vval.v_partial->pt_func; } else { - fname = rettv->v_type == VAR_FUNC ? rettv->vval.v_string + fname = rettv->v_type == VAR_FUNC || rettv->v_type == VAR_STRING + ? rettv->vval.v_string : rettv->vval.v_partial->pt_name; // Translate "s:func" to the stored function name. fname = fname_trans_sid(fname, fname_buf, &tofree, &error); @@ -21362,7 +21351,7 @@ void ex_function(exarg_T *eap) emsg_funcname(e_funcexts, name); goto erret; } - if (fp->uf_refcount > 1 || fp->uf_calls > 0) { + if (fp->uf_calls > 0) { emsg_funcname(N_("E127: Cannot redefine function %s: It is in use"), name); goto erret; @@ -21463,7 +21452,7 @@ void ex_function(exarg_T *eap) xfree(fp); goto erret; } - fp->uf_refcount = 1; + fp->uf_refcount = 1; } fp->uf_args = newargs; fp->uf_lines = newlines; @@ -21572,11 +21561,11 @@ trans_function_name( fdp->fd_di = lv.ll_di; } if (lv.ll_tv->v_type == VAR_FUNC && lv.ll_tv->vval.v_string != NULL) { - name = vim_strsave(partial_name(lv.ll_tv->vval.v_partial)); + name = vim_strsave(lv.ll_tv->vval.v_string); *pp = end; } else if (lv.ll_tv->v_type == VAR_PARTIAL && lv.ll_tv->vval.v_partial != NULL) { - name = vim_strsave(lv.ll_tv->vval.v_partial->pt_name); + name = vim_strsave(partial_name(lv.ll_tv->vval.v_partial)); *pp = end; if (partial != NULL) { *partial = lv.ll_tv->vval.v_partial; @@ -22220,7 +22209,7 @@ void ex_delfunction(exarg_T *eap) // A normal function (not a numbered function or lambda) has a // refcount of 1 for the entry in the hashtable. When deleting // it and the refcount is more than one, it should be kept. - // A numbered function or lambda snould be kept if the refcount is + // A numbered function or lambda should be kept if the refcount is // one or more. if (fp->uf_refcount > (func_name_refcount(fp->uf_name) ? 0 : 1)) { // Function is still referenced somewhere. Don't free it but @@ -22270,7 +22259,6 @@ static void func_free(ufunc_T *fp, bool force) func_remove(fp); } funccal_unref(fp->uf_scoped, fp, force); - func_remove(fp); xfree(fp); } @@ -22285,27 +22273,19 @@ void func_unref(char_u *name) if (name == NULL || !func_name_refcount(name)) { return; } - if (isdigit(*name)) { - fp = find_func(name); - if (fp == NULL) { + + fp = find_func(name); + if (fp == NULL && isdigit(*name)) { #ifdef EXITFREE - if (!entered_free_all_mem) { - EMSG2(_(e_intern2), "func_unref()"); - } + if (!entered_free_all_mem) { + EMSG2(_(e_intern2), "func_unref()"); + abort(); + } #else EMSG2(_(e_intern2), "func_unref()"); + abort(); #endif - } else { - user_func_unref(fp); - } - } else if (STRNCMP(name, "<lambda>", 8) == 0) { - // fail silently, when lambda function isn't found - fp = find_func(name); - } - if (fp == NULL && isdigit(*name)) { - EMSG2(_(e_intern2), "func_unref()"); } - if (fp != NULL && --fp->uf_refcount <= 0) { // Only delete it when it's not being used. Otherwise it's done // when "uf_calls" becomes zero. @@ -22357,13 +22337,13 @@ void func_ptr_ref(ufunc_T *fp) /// Call a user function. static void call_user_func( - ufunc_T *fp, /* pointer to function */ - int argcount, /* nr of args */ - typval_T *argvars, /* arguments */ - typval_T *rettv, /* return value */ - linenr_T firstline, /* first line of range */ - linenr_T lastline, /* last line of range */ - dict_T *selfdict /* Dictionary for "self" */ + ufunc_T *fp, // pointer to function + int argcount, // nr of args + typval_T *argvars, // arguments + typval_T *rettv, // return value + linenr_T firstline, // first line of range + linenr_T lastline, // last line of range + dict_T *selfdict // Dictionary for "self" ) { char_u *save_sourcing_name; @@ -22770,7 +22750,6 @@ static void funccal_unref(funccall_T *fc, ufunc_T *fp, bool force) } for (i = 0; i < fc->fc_funcs.ga_len; i++) { if (((ufunc_T **)(fc->fc_funcs.ga_data))[i] == fp) { - func_ptr_unref(fc->func); ((ufunc_T **)(fc->fc_funcs.ga_data))[i] = NULL; } } diff --git a/src/nvim/eval/typval_encode.c.h b/src/nvim/eval/typval_encode.c.h index 34f88cbc98..4ff5589887 100644 --- a/src/nvim/eval/typval_encode.c.h +++ b/src/nvim/eval/typval_encode.c.h @@ -344,7 +344,7 @@ static int _TYPVAL_ENCODE_CONVERT_ONE_VALUE( case VAR_PARTIAL: { partial_T *const pt = tv->vval.v_partial; (void)pt; - TYPVAL_ENCODE_CONV_FUNC_START(tv, (pt == NULL ? NULL : pt->pt_name)); + TYPVAL_ENCODE_CONV_FUNC_START(tv, (pt == NULL ? NULL : partial_name(pt))); _mp_push(*mpstack, ((MPConvStackVal) { .type = kMPConvPartial, .tv = tv, diff --git a/src/nvim/eval_defs.h b/src/nvim/eval_defs.h index ffaeb14e22..5214396f4d 100644 --- a/src/nvim/eval_defs.h +++ b/src/nvim/eval_defs.h @@ -6,10 +6,9 @@ #include "nvim/hashtab.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 +#include "nvim/garray.h" // for garray_T +#include "nvim/profile.h" // for proftime_T +#include "nvim/pos.h" // for linenr_T typedef int varnumber_T; typedef double float_T; @@ -118,9 +117,9 @@ typedef struct { // Also used for a variable. // The key is copied into "di_key" to avoid an extra alloc/free for it. struct dictitem_S { - typval_T di_tv; // type and value of the variable - char_u di_flags; // flags (only used for variable) - char_u di_key[1]; // key (actually longer!) + typval_T di_tv; ///< type and value of the variable + char_u di_flags; ///< flags (only used for variable) + char_u di_key[1]; ///< key (actually longer!) }; typedef struct dictitem_S dictitem_T; @@ -198,30 +197,29 @@ struct ufunc { // 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 + 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; // number of user functions that reference + 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; ///< number of user functions that reference // this funccal - int fc_copyID; // for garbage collection - garray_T fc_funcs; // list of ufunc_T* which keep a reference + int fc_copyID; ///< for garbage collection + garray_T fc_funcs; ///< list of ufunc_T* which keep a reference // to "func" }; diff --git a/src/nvim/regexp.c b/src/nvim/regexp.c index 269e21936c..1cd334abcd 100644 --- a/src/nvim/regexp.c +++ b/src/nvim/regexp.c @@ -6444,7 +6444,8 @@ static int submatch_line_lbr; /// Put the submatches in "argv[0]" which is a list passed into call_func() by /// vim_regsub_both(). -static int fill_submatch_list(int argc, typval_T *argv, int argcount) { +static int fill_submatch_list(int argc, typval_T *argv, int argcount) +{ listitem_T *li; int i; char_u *s; diff --git a/src/nvim/testdir/Makefile b/src/nvim/testdir/Makefile index 70163739bf..6f4e0fe49f 100644 --- a/src/nvim/testdir/Makefile +++ b/src/nvim/testdir/Makefile @@ -34,7 +34,7 @@ NEW_TESTS ?= \ test_cscope.res \ test_digraph.res \ test_diffmode.res \ - test_filter_map.res \ + test_filter_map.res \ test_gn.res \ test_hardcopy.res \ test_help_tagjump.res \ diff --git a/src/nvim/testdir/test_lambda.vim b/src/nvim/testdir/test_lambda.vim index 00665810bd..311cc6e2cb 100644 --- a/src/nvim/testdir/test_lambda.vim +++ b/src/nvim/testdir/test_lambda.vim @@ -18,29 +18,29 @@ function! Test_lambda_with_sort() call assert_equal([1, 2, 3, 4, 7], sort([3,7,2,1,4], {a, b -> a - b})) endfunction -" function! Test_lambda_with_timer() -" if !has('timers') -" return -" endif - -" let s:n = 0 -" let s:timer_id = 0 -" function! s:Foo() -" "let n = 0 -" let s:timer_id = timer_start(50, {-> execute("let s:n += 1 | echo s:n", "")}, {"repeat": -1}) -" endfunction - -" call s:Foo() -" sleep 200ms -" " do not collect lambda -" call garbagecollect() -" let m = s:n -" sleep 200ms -" call timer_stop(s:timer_id) -" call assert_true(m > 1) -" call assert_true(s:n > m + 1) -" call assert_true(s:n < 9) -" endfunction +function! Test_lambda_with_timer() + if !has('timers') + return + endif + + let s:n = 0 + let s:timer_id = 0 + function! s:Foo() + "let n = 0 + let s:timer_id = timer_start(50, {-> execute("let s:n += 1 | echo s:n", "")}, {"repeat": -1}) + endfunction + + call s:Foo() + sleep 200ms + " do not collect lambda + call garbagecollect() + let m = s:n + sleep 200ms + call timer_stop(s:timer_id) + call assert_true(m > 1) + call assert_true(s:n > m + 1) + call assert_true(s:n < 9) +endfunction function! Test_lambda_with_partial() let l:Cb = function({... -> ['zero', a:1, a:2, a:3]}, ['one', 'two']) @@ -259,10 +259,10 @@ endfunction func Test_closure_refcount() let g:Count = LambdaFoo() - call garbagecollect() + call test_garbagecollect_now() call assert_equal(1, g:Count()) let g:Count2 = LambdaFoo() - call garbagecollect() + call test_garbagecollect_now() call assert_equal(1, g:Count2()) call assert_equal(2, g:Count()) call assert_equal(3, g:Count2()) @@ -271,6 +271,7 @@ func Test_closure_refcount() delfunc LambdaBar endfunc +" This test is causing a use-after-free on shutdown. func Test_named_function_closure() func! Afoo() let x = 14 |