aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/nvim/eval.c199
-rw-r--r--src/nvim/eval/typval_encode.c.h2
-rw-r--r--src/nvim/eval_defs.h56
-rw-r--r--src/nvim/regexp.c3
-rw-r--r--src/nvim/testdir/Makefile2
-rw-r--r--src/nvim/testdir/test_lambda.vim51
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(&current_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