vim-patch:7.4.2119

Problem:    Closures are not supported.
Solution:   Capture variables in lambdas from the outer scope. (Yasuhiro
            Matsumoto, Ken Takata)

1e96d9bf98
This commit is contained in:
Michael Ennen 2016-12-17 13:07:37 -07:00
parent 6563d85990
commit 9f6f7fe26d
6 changed files with 320 additions and 88 deletions

View File

@ -1186,7 +1186,7 @@ the following ways:
1. The body of the lambda expression is an |expr1| and not a sequence of |Ex| 1. The body of the lambda expression is an |expr1| and not a sequence of |Ex|
commands. commands.
2. The prefix "a:" is optional for arguments. E.g.: > 2. The prefix "a:" should not be used for arguments. E.g.: >
:let F = {arg1, arg2 -> arg1 - arg2} :let F = {arg1, arg2 -> arg1 - arg2}
:echo F(5, 2) :echo F(5, 2)
< 3 < 3
@ -1195,6 +1195,18 @@ The arguments are optional. Example: >
:let F = {-> 'error function'} :let F = {-> 'error function'}
:echo F() :echo F()
< error function < error function
*closure*
Lambda expressions can access outer scope variables and arguments. This is
often called a closure. Example where "i" a and "a:arg" are used in a lambda
while they exists in the function scope. They remain valid even after the
function returns: >
:function Foo(arg)
: let i = 3
: return {x -> x + i - a:arg}
:endfunction
:let Bar = Foo(4)
:echo Bar(6)
< 5
Examples for using a lambda expression with |sort()|, |map()| and |filter()|: > Examples for using a lambda expression with |sort()|, |map()| and |filter()|: >
:echo map([1, 2, 3], {idx, val -> val + 1}) :echo map([1, 2, 3], {idx, val -> val + 1})
@ -1212,6 +1224,12 @@ The lambda expression is also useful for Channel, Job and timer: >
Note how execute() is used to execute an Ex command. That's ugly though. Note how execute() is used to execute an Ex command. That's ugly though.
Lambda expressions have internal names like '<lambda>42'. If you get an error
for a lambda expression, you can find what it is with the following command: >
:function {'<lambda>42'}
See also: |numbered-function|
============================================================================== ==============================================================================
3. Internal variable *internal-variables* *E461* 3. Internal variable *internal-variables* *E461*

View File

@ -223,14 +223,15 @@ static garray_T ga_loaded = {0, 0, sizeof(char_u *), 4, NULL};
static dict_T *first_dict = NULL; // list of all dicts static dict_T *first_dict = NULL; // list of all dicts
static list_T *first_list = NULL; // list of all lists static list_T *first_list = NULL; // list of all lists
#define FLEN_FIXED 40
#define FUNCARG(fp, j) ((char_u **)(fp->uf_args.ga_data))[j] #define FUNCARG(fp, j) ((char_u **)(fp->uf_args.ga_data))[j]
#define FUNCLINE(fp, j) ((char_u **)(fp->uf_lines.ga_data))[j] #define FUNCLINE(fp, j) ((char_u **)(fp->uf_lines.ga_data))[j]
#define VAR_SHORT_LEN 20 /* short variable name length */ #define VAR_SHORT_LEN 20 /* short variable name length */
#define FIXVAR_CNT 12 /* number of fixed variables */ #define FIXVAR_CNT 12 /* number of fixed variables */
/* structure to hold info for a function that is currently being executed. */ // structure to hold info for a function that is currently being executed.
typedef struct funccall_S funccall_T;
struct funccall_S { struct funccall_S {
ufunc_T *func; /* function being called */ ufunc_T *func; /* function being called */
@ -241,18 +242,21 @@ struct funccall_S {
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;
int fc_copyID; // for garbage collection
garray_T fc_funcs; // list of ufunc_T* which refer this
}; };
/* /*
@ -4358,6 +4362,11 @@ static int eval7(
} else { } else {
if (**arg == '(') { // recursive! if (**arg == '(') { // recursive!
partial_T *partial; partial_T *partial;
if (!evaluate) {
check_vars(s, len);
}
// If "s" is the name of a variable of type VAR_FUNC // If "s" is the name of a variable of type VAR_FUNC
// use its contents. // use its contents.
s = deref_func_name(s, &len, &partial, !evaluate); s = deref_func_name(s, &len, &partial, !evaluate);
@ -4387,6 +4396,7 @@ static int eval7(
} else if (evaluate) { } else if (evaluate) {
ret = get_var_tv(s, len, rettv, NULL, true, false); ret = get_var_tv(s, len, rettv, NULL, true, false);
} else { } else {
check_vars(s, len);
ret = OK; ret = OK;
} }
} }
@ -6255,6 +6265,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, copyID);
if (pt->pt_dict != NULL) { if (pt->pt_dict != NULL) {
typval_T dtv; typval_T dtv;
@ -6271,6 +6282,8 @@ 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, copyID);
break;
case VAR_UNKNOWN: case VAR_UNKNOWN:
case VAR_SPECIAL: case VAR_SPECIAL:
case VAR_FLOAT: case VAR_FLOAT:
@ -6282,6 +6295,39 @@ bool set_ref_in_item(typval_T *tv, int copyID, ht_stack_T **ht_stack,
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.
int set_ref_in_func(char_u *name, int copyID)
{
ufunc_T *fp;
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) {
return false;
}
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) {
if (fc->fc_copyID != copyID) {
fc->fc_copyID = copyID;
abort = set_ref_in_ht(&fc->l_vars.dv_hashtab, copyID, NULL);
abort = set_ref_in_ht(&fc->l_avars.dv_hashtab, copyID, NULL);
}
}
}
xfree(tofree);
return abort;
}
/// Mark all lists and dicts referenced in given mark /// Mark all lists and dicts referenced in given mark
/// ///
/// @returns true if setting references failed somehow. /// @returns true if setting references failed somehow.
@ -6971,6 +7017,7 @@ static int get_lambda_tv(char_u **arg, typval_T *rettv, int evaluate)
{ {
garray_T newargs; garray_T newargs;
garray_T newlines; garray_T newlines;
garray_T *pnewargs;
ufunc_T *fp = NULL; ufunc_T *fp = NULL;
int varargs; int varargs;
int ret; int ret;
@ -6978,6 +7025,8 @@ static int get_lambda_tv(char_u **arg, typval_T *rettv, int evaluate)
char_u *start = skipwhite(*arg + 1); char_u *start = skipwhite(*arg + 1);
char_u *s, *e; char_u *s, *e;
static int lambda_no = 0; static int lambda_no = 0;
int *old_eval_lavars = eval_lavars_used;
int eval_lavars = false;
// TODO(mike): What lengths should be used here? // TODO(mike): What lengths should be used here?
ga_init(&newargs, (int)sizeof(char_u *), 80); ga_init(&newargs, (int)sizeof(char_u *), 80);
@ -6990,12 +7039,22 @@ static int get_lambda_tv(char_u **arg, typval_T *rettv, int evaluate)
} }
// Parse the arguments again. // Parse the arguments again.
if (evaluate) {
pnewargs = &newargs;
} else {
pnewargs = NULL;
}
*arg = skipwhite(*arg + 1); *arg = skipwhite(*arg + 1);
ret = get_function_args(arg, '-', &newargs, &varargs, false); ret = get_function_args(arg, '-', pnewargs, &varargs, false);
if (ret == FAIL || **arg != '>') { if (ret == FAIL || **arg != '>') {
goto errret; goto errret;
} }
// Set up dictionaries for checking local variables and arguments.
if (evaluate) {
eval_lavars_used = &eval_lavars;
}
// Get the start and the end of the expression. // Get the start and the end of the expression.
*arg = skipwhite(*arg + 1); *arg = skipwhite(*arg + 1);
s = *arg; s = *arg;
@ -7014,18 +7073,17 @@ static int get_lambda_tv(char_u **arg, typval_T *rettv, int evaluate)
int len; int len;
char_u *p; char_u *p;
fp = (ufunc_T *)xmalloc((unsigned)(sizeof(ufunc_T) + 20)); snprintf((char *)name, sizeof(name), "<lambda>%d", lambda_no++);
fp = (ufunc_T *)xmalloc((unsigned)(sizeof(ufunc_T) + STRLEN(name)));
if (fp == NULL) { if (fp == NULL) {
goto errret; goto errret;
} }
snprintf((char *)name, sizeof(name), "<lambda>%d", lambda_no++);
ga_init(&newlines, (int)sizeof(char_u *), 1); ga_init(&newlines, (int)sizeof(char_u *), 1);
ga_grow(&newlines, 1); ga_grow(&newlines, 1);
// Add "return " before the expression. // Add "return " before the expression.
// TODO(vim): Support multiple expressions.
len = 7 + e - s + 1; len = 7 + e - s + 1;
p = (char_u *)xmalloc(len); p = (char_u *)xmalloc(len);
if (p == NULL) { if (p == NULL) {
@ -7041,8 +7099,17 @@ static int get_lambda_tv(char_u **arg, typval_T *rettv, int evaluate)
hash_add(&func_hashtab, UF2HIKEY(fp)); hash_add(&func_hashtab, UF2HIKEY(fp));
fp->uf_args = newargs; fp->uf_args = newargs;
fp->uf_lines = newlines; fp->uf_lines = newlines;
if (current_funccal != NULL && eval_lavars) {
fp->uf_scoped = current_funccal;
current_funccal->fc_refcount++;
ga_grow(&current_funccal->fc_funcs, 1);
((ufunc_T **)current_funccal->fc_funcs.ga_data)
[current_funccal->fc_funcs.ga_len++] = fp;
func_ref(current_funccal->func->uf_name);
} else {
fp->uf_scoped = NULL;
}
#ifdef FEAT_PROFILE
fp->uf_tml_count = NULL; fp->uf_tml_count = NULL;
fp->uf_tml_total = NULL; fp->uf_tml_total = NULL;
fp->uf_tml_self = NULL; fp->uf_tml_self = NULL;
@ -7050,7 +7117,6 @@ static int get_lambda_tv(char_u **arg, typval_T *rettv, int evaluate)
if (prof_def_func()) { if (prof_def_func()) {
func_do_profile(fp); func_do_profile(fp);
} }
#endif
fp->uf_varargs = true; fp->uf_varargs = true;
fp->uf_flags = 0; fp->uf_flags = 0;
fp->uf_calls = 0; fp->uf_calls = 0;
@ -7058,17 +7124,16 @@ static int get_lambda_tv(char_u **arg, typval_T *rettv, int evaluate)
rettv->vval.v_string = vim_strsave(name); rettv->vval.v_string = vim_strsave(name);
rettv->v_type = VAR_FUNC; rettv->v_type = VAR_FUNC;
} else { }
ga_clear_strings(&newargs); eval_lavars_used = old_eval_lavars;
} return OK;
return OK;
errret: errret:
ga_clear_strings(&newargs); ga_clear_strings(&newargs);
ga_clear_strings(&newlines); ga_clear_strings(&newlines);
xfree(fp); xfree(fp);
return FAIL; eval_lavars_used = old_eval_lavars;
return FAIL;
} }
/// Convert the string to a floating point number /// Convert the string to a floating point number
@ -19204,13 +19269,37 @@ get_var_tv (
return ret; return ret;
} }
/* /// Check if variable "name[len]" is a local variable or an argument.
* Handle expr[expr], expr[expr:expr] subscript and .name lookup. /// If so, "*eval_lavars_used" is set to TRUE.
* Also handle function call with Funcref variable: func(expr) static void check_vars(char_u *name, int len)
* Can all be combined: dict.func(expr)[idx]['func'](expr) {
*/ int cc;
static int char_u *varname;
handle_subscript ( hashtab_T *ht;
if (eval_lavars_used == NULL) {
return;
}
// truncate the name, so that we can use strcmp()
cc = name[len];
name[len] = NUL;
ht = find_var_ht(name, &varname);
if (ht == get_funccal_local_ht() || ht == get_funccal_args_ht()) {
if (find_var(name, NULL, true) != NULL) {
*eval_lavars_used = true;
}
}
name[len] = cc;
}
/// Handle expr[expr], expr[expr:expr] subscript and .name lookup.
/// Also handle function call with Funcref variable: func(expr)
/// Can all be combined: dict.func(expr)[idx]['func'](expr)
static int
handle_subscript(
char_u **arg, char_u **arg,
typval_T *rettv, typval_T *rettv,
int evaluate, /* do more than finding the end */ int evaluate, /* do more than finding the end */
@ -19845,19 +19934,29 @@ static dictitem_T *find_var(char_u *name, hashtab_T **htp, int no_autoload)
{ {
char_u *varname; char_u *varname;
hashtab_T *ht; hashtab_T *ht;
dictitem_T *ret = NULL;
ht = find_var_ht(name, &varname); ht = find_var_ht(name, &varname);
if (htp != NULL) if (htp != NULL) {
*htp = ht; *htp = ht;
if (ht == NULL) }
if (ht == NULL) {
return NULL; return NULL;
return find_var_in_ht(ht, *name, varname, no_autoload || htp != NULL); }
ret = find_var_in_ht(ht, *name, varname, no_autoload || htp != NULL);
if (ret != NULL) {
return ret;
}
// Search in parent scope for lambda
return find_var_in_scoped_ht(name, varname ? &varname : NULL,
no_autoload || htp != NULL);
} }
/// Find variable "varname" in hashtab "ht" with name "htname". /// Find variable "varname" in hashtab "ht" with name "htname".
/// Returns NULL if not found. /// Returns NULL if not found.
static dictitem_T *find_var_in_ht(hashtab_T *ht, int htname, dictitem_T *find_var_in_ht(hashtab_T *ht, int htname,
char_u *varname, bool no_autoload) char_u *varname, bool no_autoload)
{ {
hashitem_T *hi; hashitem_T *hi;
@ -19916,6 +20015,26 @@ static funccall_T *get_funccal(void)
return funccal; return funccal;
} }
/// Return the hashtable used for argument in the current funccal.
/// Return NULL if there is no current funccal.
hashtab_T *get_funccal_args_ht(void)
{
if (current_funccal == NULL) {
return NULL;
}
return &get_funccal()->l_avars.dv_hashtab;
}
/// Return the hashtable used for local variables in the current funccal.
/// Return NULL if there is no current funccal.
hashtab_T *get_funccal_local_ht(void)
{
if (current_funccal == NULL) {
return NULL;
}
return &get_funccal()->l_vars.dv_hashtab;
}
// Find the dict and hashtable used for a variable name. Set "varname" to the // Find the dict and hashtable used for a variable name. Set "varname" to the
// start of name without ':'. // start of name without ':'.
static hashtab_T *find_var_ht_dict(char_u *name, uint8_t **varname, dict_T **d) static hashtab_T *find_var_ht_dict(char_u *name, uint8_t **varname, dict_T **d)
@ -20198,7 +20317,12 @@ set_var (
EMSG2(_(e_illvar), name); EMSG2(_(e_illvar), name);
return; return;
} }
v = find_var_in_ht(ht, 0, varname, TRUE); v = find_var_in_ht(ht, 0, varname, true);
// Search in parent scope which is possible to reference from lambda
if (v == NULL) {
v = find_var_in_scoped_ht(name, varname ? &varname : NULL, true);
}
if ((tv->v_type == VAR_FUNC || tv->v_type == VAR_PARTIAL) if ((tv->v_type == VAR_FUNC || tv->v_type == VAR_PARTIAL)
&& var_check_func_name(name, v == NULL)) { && var_check_func_name(name, v == NULL)) {
@ -21222,6 +21346,7 @@ void ex_function(exarg_T *eap)
fp->uf_refcount = 1; fp->uf_refcount = 1;
fp->uf_args = newargs; fp->uf_args = newargs;
fp->uf_lines = newlines; fp->uf_lines = newlines;
fp->uf_scoped = NULL;
fp->uf_tml_count = NULL; fp->uf_tml_count = NULL;
fp->uf_tml_total = NULL; fp->uf_tml_total = NULL;
fp->uf_tml_self = NULL; fp->uf_tml_self = NULL;
@ -21944,13 +22069,12 @@ static void func_free(ufunc_T *fp)
xfree(fp->uf_tml_total); xfree(fp->uf_tml_total);
xfree(fp->uf_tml_self); xfree(fp->uf_tml_self);
/* remove the function from the function hashtable */ // only remove it when not done already, otherwise we would remove a newer
hi = hash_find(&func_hashtab, UF2HIKEY(fp)); // version of the function
if (HASHITEM_EMPTY(hi)) if ((fp->uf_flags & (FC_DELETED | FC_REMOVED)) == 0) {
EMSG2(_(e_intern2), "func_free()"); func_remove(fp);
else }
hash_remove(&func_hashtab, hi); funccal_unref(fp->uf_scoped, fp);
xfree(fp); xfree(fp);
} }
@ -22088,6 +22212,12 @@ call_user_func (
fc->breakpoint = dbg_find_breakpoint(FALSE, fp->uf_name, (linenr_T)0); fc->breakpoint = dbg_find_breakpoint(FALSE, fp->uf_name, (linenr_T)0);
fc->dbg_tick = debug_tick; 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_ref(fp->uf_name);
if (STRNCMP(fp->uf_name, "<lambda>", 8) == 0) { if (STRNCMP(fp->uf_name, "<lambda>", 8) == 0) {
islambda = true; islambda = true;
} }
@ -22147,7 +22277,6 @@ call_user_func (
(varnumber_T)lastline); (varnumber_T)lastline);
for (int i = 0; i < argcount; i++) { for (int i = 0; i < argcount; i++) {
bool addlocal = false; bool addlocal = false;
dictitem_T *v2;
ai = i - fp->uf_args.ga_len; ai = i - fp->uf_args.ga_len;
if (ai < 0) { if (ai < 0) {
@ -22164,39 +22293,24 @@ call_user_func (
if (fixvar_idx < FIXVAR_CNT && STRLEN(name) <= VAR_SHORT_LEN) { if (fixvar_idx < FIXVAR_CNT && STRLEN(name) <= VAR_SHORT_LEN) {
v = &fc->fixvar[fixvar_idx++].var; v = &fc->fixvar[fixvar_idx++].var;
v->di_flags = DI_FLAGS_RO | DI_FLAGS_FIX; v->di_flags = DI_FLAGS_RO | DI_FLAGS_FIX;
if (addlocal) {
v2 = v;
}
} else { } else {
v = xmalloc(sizeof(dictitem_T) + STRLEN(name)); v = xmalloc(sizeof(dictitem_T) + STRLEN(name));
v->di_flags = DI_FLAGS_RO | DI_FLAGS_FIX | DI_FLAGS_ALLOC; v->di_flags = DI_FLAGS_RO | DI_FLAGS_FIX | DI_FLAGS_ALLOC;
if (addlocal) {
v2 = (dictitem_T *)xmalloc((unsigned)(sizeof(dictitem_T)
+ STRLEN(name)));
if (v2 == NULL) {
xfree(v);
break;
}
v2->di_flags = DI_FLAGS_RO | DI_FLAGS_FIX | DI_FLAGS_ALLOC;
}
} }
STRCPY(v->di_key, name); STRCPY(v->di_key, name);
hash_add(&fc->l_avars.dv_hashtab, DI2HIKEY(v));
/* Note: the values are copied directly to avoid alloc/free. // Note: the values are copied directly to avoid alloc/free.
* "argvars" must have VAR_FIXED for v_lock. */ // "argvars" must have VAR_FIXED for v_lock.
v->di_tv = argvars[i]; v->di_tv = argvars[i];
v->di_tv.v_lock = VAR_FIXED; v->di_tv.v_lock = VAR_FIXED;
// Named arguments can be accessed without the "a:" prefix in lambda
// expressions. Add to the l: dict.
if (addlocal) { if (addlocal) {
STRCPY(v2->di_key, name); // Named arguments can be accessed without the "a:" prefix in lambda
copy_tv(&v->di_tv, &v2->di_tv); // expressions. Add to the l: dict.
v2->di_tv.v_lock = VAR_FIXED; copy_tv(&v->di_tv, &v->di_tv);
hash_add(&fc->l_vars.dv_hashtab, DI2HIKEY(v2)); hash_add(&fc->l_vars.dv_hashtab, DI2HIKEY(v));
} else {
hash_add(&fc->l_avars.dv_hashtab, DI2HIKEY(v));
} }
if (ai >= 0 && ai < MAX_FUNC_ARGS) { if (ai >= 0 && ai < MAX_FUNC_ARGS) {
@ -22388,8 +22502,9 @@ call_user_func (
* free the funccall_T and what's in it. */ * free the funccall_T and what's in it. */
if (fc->l_varlist.lv_refcount == DO_NOT_FREE_CNT if (fc->l_varlist.lv_refcount == DO_NOT_FREE_CNT
&& fc->l_vars.dv_refcount == DO_NOT_FREE_CNT && fc->l_vars.dv_refcount == DO_NOT_FREE_CNT
&& fc->l_avars.dv_refcount == DO_NOT_FREE_CNT) { && fc->l_avars.dv_refcount == DO_NOT_FREE_CNT
free_funccal(fc, FALSE); && fc->fc_refcount <= 0) {
free_funccal(fc, false);
} else { } else {
hashitem_T *hi; hashitem_T *hi;
listitem_T *li; listitem_T *li;
@ -22429,15 +22544,53 @@ call_user_func (
restore_search_patterns(); restore_search_patterns();
} }
/* /// Unreference "fc": decrement the reference count and free it when it
* Return TRUE if items in "fc" do not have "copyID". That means they are not /// becomes zero. If "fp" is not NULL, "fp" is detached from "fc".
* referenced from anywhere that is in use. static void funccal_unref(funccall_T *fc, ufunc_T *fp)
*/ {
funccall_T **pfc;
int i;
int freed = false;
if (fc == NULL) {
return;
}
if (--fc->fc_refcount <= 0) {
for (pfc = &previous_funccal; *pfc != NULL; ) {
if (fc == *pfc
&& fc->l_varlist.lv_refcount == DO_NOT_FREE_CNT
&& fc->l_vars.dv_refcount == DO_NOT_FREE_CNT
&& fc->l_avars.dv_refcount == DO_NOT_FREE_CNT) {
*pfc = fc->caller;
free_funccal(fc, true);
freed = true;
} else {
pfc = &(*pfc)->caller;
}
}
if (!freed) {
func_unref(fc->func->uf_name);
if (fp != NULL) {
for (i = 0; i < fc->fc_funcs.ga_len; i++) {
if (((ufunc_T **)(fc->fc_funcs.ga_data))[i] == fp) {
((ufunc_T **)(fc->fc_funcs.ga_data))[i] = NULL;
}
}
}
}
}
}
/// Return TRUE if items in "fc" do not have "copyID". That means they are not
/// referenced from anywhere that is in use.
static int can_free_funccal(funccall_T *fc, int copyID) static int can_free_funccal(funccall_T *fc, int copyID)
{ {
return fc->l_varlist.lv_copyID != copyID return fc->l_varlist.lv_copyID != copyID
&& fc->l_vars.dv_copyID != copyID && fc->l_vars.dv_copyID != copyID
&& fc->l_avars.dv_copyID != copyID; && fc->l_avars.dv_copyID != copyID
&& fc->fc_copyID != copyID;
} }
/* /*
@ -22451,18 +22604,38 @@ free_funccal (
{ {
listitem_T *li; listitem_T *li;
/* The a: variables typevals may not have been allocated, only free the for (int i = 0; i < fc->fc_funcs.ga_len; i++) {
* allocated variables. */ ufunc_T *fp = ((ufunc_T **)(fc->fc_funcs.ga_data))[i];
if (fp != NULL) {
fp->uf_scoped = NULL;
}
}
// The a: variables typevals may not have been allocated, only free the
// allocated variables.
vars_clear_ext(&fc->l_avars.dv_hashtab, free_val); vars_clear_ext(&fc->l_avars.dv_hashtab, free_val);
/* free all l: variables */ /* free all l: variables */
vars_clear(&fc->l_vars.dv_hashtab); vars_clear(&fc->l_vars.dv_hashtab);
/* Free the a:000 variables if they were allocated. */ // Free the a:000 variables if they were allocated.
if (free_val) if (free_val) {
for (li = fc->l_varlist.lv_first; li != NULL; li = li->li_next) for (li = fc->l_varlist.lv_first; li != NULL; li = li->li_next) {
clear_tv(&li->li_tv); clear_tv(&li->li_tv);
}
}
for (int i = 0; i < fc->fc_funcs.ga_len; i++) {
ufunc_T *fp = ((ufunc_T **)(fc->fc_funcs.ga_data))[i];
if (fp != NULL) {
func_unref(fc->func->uf_name);
}
}
ga_clear(&fc->fc_funcs);
func_unref(fc->func->uf_name);
xfree(fc); xfree(fc);
} }
@ -22781,6 +22954,39 @@ static var_flavour_T var_flavour(char_u *varname)
} }
} }
/// Search variable in parent scope.
dictitem_T *find_var_in_scoped_ht(char_u *name, char_u **varname,
int no_autoload)
{
dictitem_T *v = NULL;
funccall_T *old_current_funccal = current_funccal;
hashtab_T *ht;
if (current_funccal == NULL || current_funccal->func->uf_scoped == NULL) {
return NULL;
}
// Search in parent scope which is possible to reference from lambda
current_funccal = current_funccal->func->uf_scoped;
while (current_funccal) {
ht = find_var_ht(name, varname ? &(*varname) : NULL);
if (ht != NULL) {
v = find_var_in_ht(ht, *name,
varname ? *varname : NULL, no_autoload);
if (v != NULL) {
break;
}
}
if (current_funccal == current_funccal->func->uf_scoped) {
break;
}
current_funccal = current_funccal->func->uf_scoped;
}
current_funccal = old_current_funccal;
return v;
}
/// Iterate over global variables /// Iterate over global variables
/// ///
/// @warning No modifications to global variable dictionary must be performed /// @warning No modifications to global variable dictionary must be performed

View File

@ -13,6 +13,8 @@
// All user-defined functions are found in this hashtable. // All user-defined functions are found in this hashtable.
extern hashtab_T func_hashtab; extern hashtab_T func_hashtab;
typedef struct funccall_S funccall_T;
// Structure to hold info for a user function. // Structure to hold info for a user function.
typedef struct ufunc ufunc_T; typedef struct ufunc ufunc_T;
@ -40,6 +42,7 @@ struct ufunc {
scid_T uf_script_ID; ///< ID of script where function was defined, scid_T uf_script_ID; ///< ID of script where function was defined,
// used for s: variables // used for s: variables
int uf_refcount; ///< for numbered function: reference count int uf_refcount; ///< for numbered function: reference count
funccall_T *uf_scoped; ///< l: local variables for closure
char_u uf_name[1]; ///< name of function (actually longer); can char_u uf_name[1]; ///< name of function (actually longer); can
// start with <SNR>123_ (<SNR> is K_SPECIAL // start with <SNR>123_ (<SNR> is K_SPECIAL
// KS_EXTRA KE_SNR) // KS_EXTRA KE_SNR)

View File

@ -1231,6 +1231,9 @@ EXTERN char *ignoredp;
EXTERN bool in_free_unref_items INIT(= false); EXTERN bool in_free_unref_items INIT(= false);
// Used for checking if local variables or arguments used in a lambda.
EXTERN int *eval_lavars_used INIT(= NULL);
// If a msgpack-rpc channel should be started over stdin/stdout // If a msgpack-rpc channel should be started over stdin/stdout
EXTERN bool embedded_mode INIT(= false); EXTERN bool embedded_mode INIT(= false);

View File

@ -284,3 +284,5 @@ func Test_named_function_closure()
call garbagecollect() call garbagecollect()
call assert_equal(14, s:Abar()) call assert_equal(14, s:Abar())
endfunc endfunc
=======
>>>>>>> 42b34811... vim-patch:7.4.2119

View File

@ -321,7 +321,7 @@ static int included_patches[] = {
// 2122 NA // 2122 NA
// 2121, // 2121,
// 2120, // 2120,
// 2119, 2119,
// 2118 NA // 2118 NA
2117, 2117,
// 2116 NA // 2116 NA