mirror of
https://github.com/neovim/neovim.git
synced 2025-09-20 18:28:19 +00:00
Allow lambdas to be used with jobs, timers and dictwatchers.
This commit is contained in:
177
src/nvim/eval.c
177
src/nvim/eval.c
@@ -620,9 +620,8 @@ void eval_clear(void)
|
|||||||
// unreferenced lists and dicts
|
// unreferenced lists and dicts
|
||||||
(void)garbage_collect(false);
|
(void)garbage_collect(false);
|
||||||
|
|
||||||
/* functions */
|
// functions
|
||||||
free_all_functions();
|
free_all_functions();
|
||||||
hash_clear(&func_hashtab);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
@@ -5859,6 +5858,7 @@ bool garbage_collect(bool testing)
|
|||||||
// referenced through previous_funccal. This must be first, because if
|
// referenced through previous_funccal. This must be first, because if
|
||||||
// the item is referenced elsewhere the funccal must not be freed.
|
// the item is referenced elsewhere the funccal must not be freed.
|
||||||
for (funccall_T *fc = previous_funccal; fc != NULL; fc = fc->caller) {
|
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_vars.dv_hashtab, copyID + 1, NULL);
|
||||||
ABORTING(set_ref_in_ht)(&fc->l_avars.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
|
// function-local variables
|
||||||
for (funccall_T *fc = current_funccal; fc != NULL; fc = fc->caller) {
|
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_vars.dv_hashtab, copyID, NULL);
|
||||||
ABORTING(set_ref_in_ht)(&fc->l_avars.dv_hashtab, copyID, NULL);
|
ABORTING(set_ref_in_ht)(&fc->l_avars.dv_hashtab, copyID, NULL);
|
||||||
}
|
}
|
||||||
@@ -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.
|
// A partial does not have a copyID, because it cannot contain itself.
|
||||||
if (pt != NULL) {
|
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) {
|
if (pt->pt_dict != NULL) {
|
||||||
typval_T dtv;
|
typval_T dtv;
|
||||||
|
|
||||||
@@ -6265,7 +6266,7 @@ bool set_ref_in_item(typval_T *tv, int copyID, ht_stack_T **ht_stack,
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case VAR_FUNC:
|
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;
|
break;
|
||||||
case VAR_UNKNOWN:
|
case VAR_UNKNOWN:
|
||||||
case VAR_SPECIAL:
|
case VAR_SPECIAL:
|
||||||
@@ -6287,7 +6288,7 @@ bool set_ref_in_functions(int copyID)
|
|||||||
ufunc_T *fp;
|
ufunc_T *fp;
|
||||||
|
|
||||||
todo = (int)func_hashtab.ht_used;
|
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)) {
|
if (!HASHITEM_EMPTY(hi)) {
|
||||||
todo--;
|
todo--;
|
||||||
fp = HI2UF(hi);
|
fp = HI2UF(hi);
|
||||||
@@ -6299,36 +6300,7 @@ bool set_ref_in_functions(int copyID)
|
|||||||
return abort;
|
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
|
/// 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)
|
static bool set_ref_in_funccal(funccall_T *fc, int copyID)
|
||||||
{
|
{
|
||||||
int abort = false;
|
bool abort = false;
|
||||||
|
|
||||||
if (fc->fc_copyID != copyID) {
|
if (fc->fc_copyID != copyID) {
|
||||||
fc->fc_copyID = copyID;
|
fc->fc_copyID = copyID;
|
||||||
@@ -6936,7 +6908,7 @@ failret:
|
|||||||
|
|
||||||
/// Get function arguments.
|
/// Get function arguments.
|
||||||
static int get_function_args(char_u **argp, char_u endchar, garray_T *newargs,
|
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;
|
bool mustend = false;
|
||||||
char_u *arg = *argp;
|
char_u *arg = *argp;
|
||||||
@@ -6979,6 +6951,7 @@ static int get_function_args(char_u **argp, char_u endchar, garray_T *newargs,
|
|||||||
*p = NUL;
|
*p = NUL;
|
||||||
arg = vim_strsave(arg);
|
arg = vim_strsave(arg);
|
||||||
if (arg == NULL) {
|
if (arg == NULL) {
|
||||||
|
*p = c;
|
||||||
goto err_ret;
|
goto err_ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -7025,10 +6998,11 @@ err_ret:
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Register function "fp" as using "current_funccal" as its scope.
|
/// 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) {
|
if (fp->uf_scoped == current_funccal) {
|
||||||
// no change
|
// no change
|
||||||
return OK;
|
return;
|
||||||
}
|
}
|
||||||
funccal_unref(fp->uf_scoped, fp, false);
|
funccal_unref(fp->uf_scoped, fp, false);
|
||||||
fp->uf_scoped = current_funccal;
|
fp->uf_scoped = current_funccal;
|
||||||
@@ -7036,16 +7010,14 @@ static int register_closure(ufunc_T *fp) {
|
|||||||
ga_grow(¤t_funccal->fc_funcs, 1);
|
ga_grow(¤t_funccal->fc_funcs, 1);
|
||||||
((ufunc_T **)current_funccal->fc_funcs.ga_data)
|
((ufunc_T **)current_funccal->fc_funcs.ga_data)
|
||||||
[current_funccal->fc_funcs.ga_len++] = fp;
|
[current_funccal->fc_funcs.ga_len++] = fp;
|
||||||
return OK;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Parse a lambda expression and get a Funcref from "*arg".
|
/// Parse a lambda expression and get a Funcref from "*arg".
|
||||||
///
|
///
|
||||||
/// @return OK or FAIL. Returns NOTDONE for dict or {expr}.
|
/// @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 newargs = GA_EMPTY_INIT_VALUE;
|
||||||
garray_T newlines;
|
|
||||||
garray_T *pnewargs;
|
garray_T *pnewargs;
|
||||||
ufunc_T *fp = NULL;
|
ufunc_T *fp = NULL;
|
||||||
int varargs;
|
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 *old_eval_lavars = eval_lavars_used;
|
||||||
int eval_lavars = false;
|
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.
|
// First, check if this is a lambda expression. "->" must exists.
|
||||||
ret = get_function_args(&start, '-', NULL, NULL, true);
|
ret = get_function_args(&start, '-', NULL, NULL, true);
|
||||||
if (ret == FAIL || *start != '>') {
|
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 *p;
|
||||||
char_u name[20];
|
char_u name[20];
|
||||||
partial_T *pt;
|
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)));
|
fp = (ufunc_T *)xcalloc(1, sizeof(ufunc_T) + STRLEN(name));
|
||||||
if (fp == NULL) {
|
pt = (partial_T *)xcalloc(1, sizeof(partial_T));
|
||||||
goto errret;
|
|
||||||
}
|
|
||||||
pt = (partial_T *)xcalloc(1, (unsigned)(sizeof(partial_T)));
|
|
||||||
if (pt == NULL) {
|
if (pt == NULL) {
|
||||||
xfree(fp);
|
xfree(fp);
|
||||||
goto errret;
|
goto errret;
|
||||||
@@ -7121,13 +7088,9 @@ static int get_lambda_tv(char_u **arg, typval_T *rettv, int evaluate)
|
|||||||
// Add "return " before the expression.
|
// Add "return " before the expression.
|
||||||
len = 7 + e - s + 1;
|
len = 7 + e - s + 1;
|
||||||
p = (char_u *)xmalloc(len);
|
p = (char_u *)xmalloc(len);
|
||||||
if (p == NULL) {
|
|
||||||
goto errret;
|
|
||||||
}
|
|
||||||
((char_u **)(newlines.ga_data))[newlines.ga_len++] = p;
|
((char_u **)(newlines.ga_data))[newlines.ga_len++] = p;
|
||||||
STRCPY(p, "return ");
|
STRCPY(p, "return ");
|
||||||
STRNCPY(p + 7, s, e - s);
|
STRLCPY(p + 7, s, e - s + 1);
|
||||||
p[7 + e - s] = NUL;
|
|
||||||
|
|
||||||
fp->uf_refcount = 1;
|
fp->uf_refcount = 1;
|
||||||
STRCPY(fp->uf_name, name);
|
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->vval.v_partial = pt;
|
||||||
rettv->v_type = VAR_PARTIAL;
|
rettv->v_type = VAR_PARTIAL;
|
||||||
}
|
}
|
||||||
|
|
||||||
eval_lavars_used = old_eval_lavars;
|
eval_lavars_used = old_eval_lavars;
|
||||||
return OK;
|
return OK;
|
||||||
|
|
||||||
errret:
|
errret:
|
||||||
ga_clear_strings(&newargs);
|
ga_clear_strings(&newargs);
|
||||||
ga_clear_strings(&newlines);
|
|
||||||
xfree(fp);
|
xfree(fp);
|
||||||
eval_lavars_used = old_eval_lavars;
|
eval_lavars_used = old_eval_lavars;
|
||||||
return FAIL;
|
return FAIL;
|
||||||
@@ -7291,9 +7254,6 @@ char_u *get_expr_name(expand_T *xp, int idx)
|
|||||||
return get_user_var_name(xp, ++intidx);
|
return get_user_var_name(xp, ++intidx);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/// Find internal function in hash functions
|
/// Find internal function in hash functions
|
||||||
///
|
///
|
||||||
/// @param[in] name Name of the function.
|
/// @param[in] name Name of the function.
|
||||||
@@ -7490,6 +7450,37 @@ fname_trans_sid(char_u *name, char_u *fname_buf, char_u **tofree, int *error) {
|
|||||||
return fname;
|
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
|
/// Call a function with its resolved parameters
|
||||||
///
|
///
|
||||||
/// "argv_func", when not NULL, can be used to fill in arguments only when the
|
/// "argv_func", when not NULL, can be used to fill in arguments only when the
|
||||||
@@ -8927,11 +8918,6 @@ static void f_dictwatcheradd(typval_T *argvars, typval_T *rettv, FunPtr fptr)
|
|||||||
return;
|
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);
|
char *key_pattern = (char *)get_tv_string_chk(argvars + 1);
|
||||||
assert(key_pattern);
|
assert(key_pattern);
|
||||||
const size_t key_len = STRLEN(argvars[1].vval.v_string);
|
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;
|
Callback callback;
|
||||||
if (!callback_from_typval(&callback, &argvars[2])) {
|
if (!callback_from_typval(&callback, &argvars[2])) {
|
||||||
|
EMSG2(e_invarg2, "funcref");
|
||||||
return;
|
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);
|
copy_tv(tv, &vimvars[VV_VAL].vv_tv);
|
||||||
argv[0] = vimvars[VV_KEY].vv_tv;
|
argv[0] = vimvars[VV_KEY].vv_tv;
|
||||||
argv[1] = vimvars[VV_VAL].vv_tv;
|
argv[1] = vimvars[VV_VAL].vv_tv;
|
||||||
s = expr->vval.v_string;
|
|
||||||
if (expr->v_type == VAR_FUNC) {
|
if (expr->v_type == VAR_FUNC) {
|
||||||
s = expr->vval.v_string;
|
s = expr->vval.v_string;
|
||||||
if (call_func(s, (int)STRLEN(s), &rettv, 2, argv, NULL,
|
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;
|
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;
|
name = s;
|
||||||
trans_name = trans_function_name(&name, false,
|
trans_name = trans_function_name(&name, false,
|
||||||
TFN_INT | TFN_QUIET | TFN_NO_AUTOLOAD
|
TFN_INT | TFN_QUIET | TFN_NO_AUTOLOAD
|
||||||
| TFN_NO_DEREF, NULL, NULL);
|
| TFN_NO_DEREF, NULL, NULL);
|
||||||
if (name != NULL) {
|
if (*name != NUL) {
|
||||||
s = NULL;
|
s = NULL;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (s == NULL || *s == NUL || (use_string && ascii_isdigit(*s))
|
if (s == NULL || *s == NUL || (use_string && ascii_isdigit(*s))
|
||||||
|| (is_funcref && trans_name == NULL)) {
|
|| (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
|
} else if (trans_name != NULL
|
||||||
&& (is_funcref ? find_func(trans_name) == NULL
|
&& (is_funcref ? find_func(trans_name) == NULL
|
||||||
: !translated_function_exists(trans_name))) {
|
: !translated_function_exists(trans_name))) {
|
||||||
// Don't check an autoload name for existence here.
|
|
||||||
EMSG2(_("E700: Unknown function: %s"), s);
|
EMSG2(_("E700: Unknown function: %s"), s);
|
||||||
} else {
|
} else {
|
||||||
int dict_idx = 0;
|
int dict_idx = 0;
|
||||||
@@ -17733,7 +17719,8 @@ static void f_termopen(typval_T *argvars, typval_T *rettv, FunPtr fptr)
|
|||||||
}
|
}
|
||||||
|
|
||||||
// "test_garbagecollect_now()" function
|
// "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
|
// This is dangerous, any Lists and Dicts used internally may be freed
|
||||||
// while still in use.
|
// 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.
|
/// 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;
|
list_T *l = &sl->sl_list;
|
||||||
|
|
||||||
memset(sl, 0, sizeof(staticList10_T));
|
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) {
|
if (rettv->v_type == VAR_PARTIAL && rettv->vval.v_partial->pt_func != NULL) {
|
||||||
fp = rettv->vval.v_partial->pt_func;
|
fp = rettv->vval.v_partial->pt_func;
|
||||||
} else {
|
} 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;
|
: rettv->vval.v_partial->pt_name;
|
||||||
// Translate "s:func" to the stored function name.
|
// Translate "s:func" to the stored function name.
|
||||||
fname = fname_trans_sid(fname, fname_buf, &tofree, &error);
|
fname = fname_trans_sid(fname, fname_buf, &tofree, &error);
|
||||||
@@ -21362,7 +21351,7 @@ void ex_function(exarg_T *eap)
|
|||||||
emsg_funcname(e_funcexts, name);
|
emsg_funcname(e_funcexts, name);
|
||||||
goto erret;
|
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"),
|
emsg_funcname(N_("E127: Cannot redefine function %s: It is in use"),
|
||||||
name);
|
name);
|
||||||
goto erret;
|
goto erret;
|
||||||
@@ -21572,11 +21561,11 @@ trans_function_name(
|
|||||||
fdp->fd_di = lv.ll_di;
|
fdp->fd_di = lv.ll_di;
|
||||||
}
|
}
|
||||||
if (lv.ll_tv->v_type == VAR_FUNC && lv.ll_tv->vval.v_string != NULL) {
|
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;
|
*pp = end;
|
||||||
} else if (lv.ll_tv->v_type == VAR_PARTIAL
|
} else if (lv.ll_tv->v_type == VAR_PARTIAL
|
||||||
&& lv.ll_tv->vval.v_partial != NULL) {
|
&& 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;
|
*pp = end;
|
||||||
if (partial != NULL) {
|
if (partial != NULL) {
|
||||||
*partial = lv.ll_tv->vval.v_partial;
|
*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
|
// A normal function (not a numbered function or lambda) has a
|
||||||
// refcount of 1 for the entry in the hashtable. When deleting
|
// refcount of 1 for the entry in the hashtable. When deleting
|
||||||
// it and the refcount is more than one, it should be kept.
|
// 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.
|
// one or more.
|
||||||
if (fp->uf_refcount > (func_name_refcount(fp->uf_name) ? 0 : 1)) {
|
if (fp->uf_refcount > (func_name_refcount(fp->uf_name) ? 0 : 1)) {
|
||||||
// Function is still referenced somewhere. Don't free it but
|
// 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);
|
func_remove(fp);
|
||||||
}
|
}
|
||||||
funccal_unref(fp->uf_scoped, fp, force);
|
funccal_unref(fp->uf_scoped, fp, force);
|
||||||
func_remove(fp);
|
|
||||||
xfree(fp);
|
xfree(fp);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -22285,27 +22273,19 @@ void func_unref(char_u *name)
|
|||||||
if (name == NULL || !func_name_refcount(name)) {
|
if (name == NULL || !func_name_refcount(name)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (isdigit(*name)) {
|
|
||||||
fp = find_func(name);
|
fp = find_func(name);
|
||||||
if (fp == NULL) {
|
if (fp == NULL && isdigit(*name)) {
|
||||||
#ifdef EXITFREE
|
#ifdef EXITFREE
|
||||||
if (!entered_free_all_mem) {
|
if (!entered_free_all_mem) {
|
||||||
EMSG2(_(e_intern2), "func_unref()");
|
EMSG2(_(e_intern2), "func_unref()");
|
||||||
|
abort();
|
||||||
}
|
}
|
||||||
#else
|
#else
|
||||||
EMSG2(_(e_intern2), "func_unref()");
|
EMSG2(_(e_intern2), "func_unref()");
|
||||||
|
abort();
|
||||||
#endif
|
#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) {
|
if (fp != NULL && --fp->uf_refcount <= 0) {
|
||||||
// Only delete it when it's not being used. Otherwise it's done
|
// Only delete it when it's not being used. Otherwise it's done
|
||||||
// when "uf_calls" becomes zero.
|
// when "uf_calls" becomes zero.
|
||||||
@@ -22357,13 +22337,13 @@ void func_ptr_ref(ufunc_T *fp)
|
|||||||
/// Call a user function.
|
/// Call a user function.
|
||||||
static void
|
static void
|
||||||
call_user_func(
|
call_user_func(
|
||||||
ufunc_T *fp, /* pointer to function */
|
ufunc_T *fp, // pointer to function
|
||||||
int argcount, /* nr of args */
|
int argcount, // nr of args
|
||||||
typval_T *argvars, /* arguments */
|
typval_T *argvars, // arguments
|
||||||
typval_T *rettv, /* return value */
|
typval_T *rettv, // return value
|
||||||
linenr_T firstline, /* first line of range */
|
linenr_T firstline, // first line of range
|
||||||
linenr_T lastline, /* last line of range */
|
linenr_T lastline, // last line of range
|
||||||
dict_T *selfdict /* Dictionary for "self" */
|
dict_T *selfdict // Dictionary for "self"
|
||||||
)
|
)
|
||||||
{
|
{
|
||||||
char_u *save_sourcing_name;
|
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++) {
|
for (i = 0; i < fc->fc_funcs.ga_len; i++) {
|
||||||
if (((ufunc_T **)(fc->fc_funcs.ga_data))[i] == fp) {
|
if (((ufunc_T **)(fc->fc_funcs.ga_data))[i] == fp) {
|
||||||
func_ptr_unref(fc->func);
|
|
||||||
((ufunc_T **)(fc->fc_funcs.ga_data))[i] = NULL;
|
((ufunc_T **)(fc->fc_funcs.ga_data))[i] = NULL;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -344,7 +344,7 @@ static int _TYPVAL_ENCODE_CONVERT_ONE_VALUE(
|
|||||||
case VAR_PARTIAL: {
|
case VAR_PARTIAL: {
|
||||||
partial_T *const pt = tv->vval.v_partial;
|
partial_T *const pt = tv->vval.v_partial;
|
||||||
(void)pt;
|
(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) {
|
_mp_push(*mpstack, ((MPConvStackVal) {
|
||||||
.type = kMPConvPartial,
|
.type = kMPConvPartial,
|
||||||
.tv = tv,
|
.tv = tv,
|
||||||
|
@@ -6,9 +6,8 @@
|
|||||||
|
|
||||||
#include "nvim/hashtab.h"
|
#include "nvim/hashtab.h"
|
||||||
#include "nvim/lib/queue.h"
|
#include "nvim/lib/queue.h"
|
||||||
#include "nvim/garray.h" // For garray_T
|
#include "nvim/garray.h" // for garray_T
|
||||||
// for proftime_T
|
#include "nvim/profile.h" // for proftime_T
|
||||||
#include "nvim/profile.h"
|
|
||||||
#include "nvim/pos.h" // for linenr_T
|
#include "nvim/pos.h" // for linenr_T
|
||||||
|
|
||||||
typedef int varnumber_T;
|
typedef int varnumber_T;
|
||||||
@@ -118,9 +117,9 @@ typedef struct {
|
|||||||
// Also used for a variable.
|
// Also used for a variable.
|
||||||
// The key is copied into "di_key" to avoid an extra alloc/free for it.
|
// The key is copied into "di_key" to avoid an extra alloc/free for it.
|
||||||
struct dictitem_S {
|
struct dictitem_S {
|
||||||
typval_T di_tv; // type and value of the variable
|
typval_T di_tv; ///< type and value of the variable
|
||||||
char_u di_flags; // flags (only used for variable)
|
char_u di_flags; ///< flags (only used for variable)
|
||||||
char_u di_key[1]; // key (actually longer!)
|
char_u di_key[1]; ///< key (actually longer!)
|
||||||
};
|
};
|
||||||
|
|
||||||
typedef struct dictitem_S dictitem_T;
|
typedef struct dictitem_S dictitem_T;
|
||||||
@@ -198,30 +197,29 @@ struct ufunc {
|
|||||||
|
|
||||||
// structure to hold info for a function that is currently being executed.
|
// structure to hold info for a function that is currently being executed.
|
||||||
struct funccall_S {
|
struct funccall_S {
|
||||||
ufunc_T *func; // function being called
|
ufunc_T *func; ///< function being called
|
||||||
int linenr; // next line to be executed
|
int linenr; ///< next line to be executed
|
||||||
int returned; // ":return" used
|
int returned; ///< ":return" used
|
||||||
struct // fixed variables for arguments
|
struct { ///< fixed variables for arguments
|
||||||
{
|
dictitem_T var; ///< variable (without room for name)
|
||||||
dictitem_T var; // variable (without room for name)
|
char_u room[VAR_SHORT_LEN]; ///< room for the name
|
||||||
char_u room[VAR_SHORT_LEN]; // room for the name
|
|
||||||
} fixvar[FIXVAR_CNT];
|
} fixvar[FIXVAR_CNT];
|
||||||
dict_T l_vars; // l: local function variables
|
dict_T l_vars; ///< l: local function variables
|
||||||
dictitem_T l_vars_var; // variable for l: scope
|
dictitem_T l_vars_var; ///< variable for l: scope
|
||||||
dict_T l_avars; // a: argument variables
|
dict_T l_avars; ///< a: argument variables
|
||||||
dictitem_T l_avars_var; // variable for a: scope
|
dictitem_T l_avars_var; ///< variable for a: scope
|
||||||
list_T l_varlist; // list for a:000
|
list_T l_varlist; ///< list for a:000
|
||||||
listitem_T l_listitems[MAX_FUNC_ARGS]; // listitems for a:000
|
listitem_T l_listitems[MAX_FUNC_ARGS]; ///< listitems for a:000
|
||||||
typval_T *rettv; // return value
|
typval_T *rettv; ///< return value
|
||||||
linenr_T breakpoint; // next line with breakpoint or zero
|
linenr_T breakpoint; ///< next line with breakpoint or zero
|
||||||
int dbg_tick; // debug_tick when breakpoint was set
|
int dbg_tick; ///< debug_tick when breakpoint was set
|
||||||
int level; // top nesting level of executed function
|
int level; ///< top nesting level of executed function
|
||||||
proftime_T prof_child; // time spent in a child
|
proftime_T prof_child; ///< time spent in a child
|
||||||
funccall_T *caller; // calling function or NULL
|
funccall_T *caller; ///< calling function or NULL
|
||||||
int fc_refcount; // number of user functions that reference
|
int fc_refcount; ///< number of user functions that reference
|
||||||
// this funccal
|
// this funccal
|
||||||
int fc_copyID; // for garbage collection
|
int fc_copyID; ///< for garbage collection
|
||||||
garray_T fc_funcs; // list of ufunc_T* which keep a reference
|
garray_T fc_funcs; ///< list of ufunc_T* which keep a reference
|
||||||
// to "func"
|
// to "func"
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@@ -6444,7 +6444,8 @@ static int submatch_line_lbr;
|
|||||||
|
|
||||||
/// Put the submatches in "argv[0]" which is a list passed into call_func() by
|
/// Put the submatches in "argv[0]" which is a list passed into call_func() by
|
||||||
/// vim_regsub_both().
|
/// 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;
|
listitem_T *li;
|
||||||
int i;
|
int i;
|
||||||
char_u *s;
|
char_u *s;
|
||||||
|
@@ -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}))
|
call assert_equal([1, 2, 3, 4, 7], sort([3,7,2,1,4], {a, b -> a - b}))
|
||||||
endfunction
|
endfunction
|
||||||
|
|
||||||
" function! Test_lambda_with_timer()
|
function! Test_lambda_with_timer()
|
||||||
" if !has('timers')
|
if !has('timers')
|
||||||
" return
|
return
|
||||||
" endif
|
endif
|
||||||
|
|
||||||
" let s:n = 0
|
let s:n = 0
|
||||||
" let s:timer_id = 0
|
let s:timer_id = 0
|
||||||
" function! s:Foo()
|
function! s:Foo()
|
||||||
" "let n = 0
|
"let n = 0
|
||||||
" let s:timer_id = timer_start(50, {-> execute("let s:n += 1 | echo s:n", "")}, {"repeat": -1})
|
let s:timer_id = timer_start(50, {-> execute("let s:n += 1 | echo s:n", "")}, {"repeat": -1})
|
||||||
" endfunction
|
endfunction
|
||||||
|
|
||||||
" call s:Foo()
|
call s:Foo()
|
||||||
" sleep 200ms
|
sleep 200ms
|
||||||
" " do not collect lambda
|
" do not collect lambda
|
||||||
" call garbagecollect()
|
call garbagecollect()
|
||||||
" let m = s:n
|
let m = s:n
|
||||||
" sleep 200ms
|
sleep 200ms
|
||||||
" call timer_stop(s:timer_id)
|
call timer_stop(s:timer_id)
|
||||||
" call assert_true(m > 1)
|
call assert_true(m > 1)
|
||||||
" call assert_true(s:n > m + 1)
|
call assert_true(s:n > m + 1)
|
||||||
" call assert_true(s:n < 9)
|
call assert_true(s:n < 9)
|
||||||
" endfunction
|
endfunction
|
||||||
|
|
||||||
function! Test_lambda_with_partial()
|
function! Test_lambda_with_partial()
|
||||||
let l:Cb = function({... -> ['zero', a:1, a:2, a:3]}, ['one', 'two'])
|
let l:Cb = function({... -> ['zero', a:1, a:2, a:3]}, ['one', 'two'])
|
||||||
@@ -259,10 +259,10 @@ endfunction
|
|||||||
|
|
||||||
func Test_closure_refcount()
|
func Test_closure_refcount()
|
||||||
let g:Count = LambdaFoo()
|
let g:Count = LambdaFoo()
|
||||||
call garbagecollect()
|
call test_garbagecollect_now()
|
||||||
call assert_equal(1, g:Count())
|
call assert_equal(1, g:Count())
|
||||||
let g:Count2 = LambdaFoo()
|
let g:Count2 = LambdaFoo()
|
||||||
call garbagecollect()
|
call test_garbagecollect_now()
|
||||||
call assert_equal(1, g:Count2())
|
call assert_equal(1, g:Count2())
|
||||||
call assert_equal(2, g:Count())
|
call assert_equal(2, g:Count())
|
||||||
call assert_equal(3, g:Count2())
|
call assert_equal(3, g:Count2())
|
||||||
@@ -271,6 +271,7 @@ func Test_closure_refcount()
|
|||||||
delfunc LambdaBar
|
delfunc LambdaBar
|
||||||
endfunc
|
endfunc
|
||||||
|
|
||||||
|
" This test is causing a use-after-free on shutdown.
|
||||||
func Test_named_function_closure()
|
func Test_named_function_closure()
|
||||||
func! Afoo()
|
func! Afoo()
|
||||||
let x = 14
|
let x = 14
|
||||||
|
7
test.vim
7
test.vim
@@ -1,7 +0,0 @@
|
|||||||
func! Afoo()
|
|
||||||
let x = 14
|
|
||||||
func! s:Abar()
|
|
||||||
return x
|
|
||||||
endfunc
|
|
||||||
endfunc
|
|
||||||
call Afoo()
|
|
@@ -391,6 +391,27 @@ describe('jobs', function()
|
|||||||
eq({'notification', '1', {'foo', 'bar', {'some text', ''}, 'stdout'}}, next_msg())
|
eq({'notification', '1', {'foo', 'bar', {'some text', ''}, 'stdout'}}, next_msg())
|
||||||
end)
|
end)
|
||||||
|
|
||||||
|
it('jobstart() works with closures', function()
|
||||||
|
source([[
|
||||||
|
fun! MkFun()
|
||||||
|
let a1 = 'foo'
|
||||||
|
let a2 = 'bar'
|
||||||
|
return {id, data, event -> rpcnotify(g:channel, '1', a1, a2, data, event)}
|
||||||
|
endfun
|
||||||
|
let g:job_opts = {'on_stdout': MkFun()}
|
||||||
|
call jobstart(['echo'], g:job_opts)
|
||||||
|
]])
|
||||||
|
eq({'notification', '1', {'foo', 'bar', {'', ''}, 'stdout'}}, next_msg())
|
||||||
|
end)
|
||||||
|
|
||||||
|
it('jobstart() works when closure passed directly to `jobstart`', function()
|
||||||
|
source([[
|
||||||
|
let g:job_opts = {'on_stdout': {id, data, event -> rpcnotify(g:channel, '1', 'foo', 'bar', data, event)}}
|
||||||
|
call jobstart(['echo'], g:job_opts)
|
||||||
|
]])
|
||||||
|
eq({'notification', '1', {'foo', 'bar', {'', ''}, 'stdout'}}, next_msg())
|
||||||
|
end)
|
||||||
|
|
||||||
describe('jobwait', function()
|
describe('jobwait', function()
|
||||||
it('returns a list of status codes', function()
|
it('returns a list of status codes', function()
|
||||||
source([[
|
source([[
|
||||||
|
@@ -254,7 +254,6 @@ describe('dictionary change notifications', function()
|
|||||||
command('call g:ReplaceWatcher2()')
|
command('call g:ReplaceWatcher2()')
|
||||||
command('let g:key = "value"')
|
command('let g:key = "value"')
|
||||||
eq({'notification', '2b', {'key', {old = 'v2', new = 'value'}}}, next_msg())
|
eq({'notification', '2b', {'key', {old = 'v2', new = 'value'}}}, next_msg())
|
||||||
|
|
||||||
end)
|
end)
|
||||||
|
|
||||||
it('does not crash when freeing a watched dictionary', function()
|
it('does not crash when freeing a watched dictionary', function()
|
||||||
@@ -273,4 +272,15 @@ describe('dictionary change notifications', function()
|
|||||||
eq(2, eval('1+1')) -- Still alive?
|
eq(2, eval('1+1')) -- Still alive?
|
||||||
end)
|
end)
|
||||||
end)
|
end)
|
||||||
|
|
||||||
|
describe('with lambdas', function()
|
||||||
|
it('works correctly', function()
|
||||||
|
source([[
|
||||||
|
let d = {'foo': 'baz'}
|
||||||
|
call dictwatcheradd(d, 'foo', {dict, key, value -> rpcnotify(g:channel, '2', key, value)})
|
||||||
|
let d.foo = 'bar'
|
||||||
|
]])
|
||||||
|
eq({'notification', '2', {'foo', {old = 'baz', new = 'bar'}}}, next_msg())
|
||||||
|
end)
|
||||||
|
end)
|
||||||
end)
|
end)
|
||||||
|
Reference in New Issue
Block a user