0
0
mirror of https://github.com/vim/vim.git synced 2025-07-24 10:45:12 -04:00

patch 7.4.2119

Problem:    Closures are not supported.
Solution:   Capture variables in lambdas from the outer scope. (Yasuhiro
            Matsumoto, Ken Takata)
This commit is contained in:
Bram Moolenaar 2016-07-29 22:15:09 +02:00
parent 83a2a80d6f
commit 1e96d9bf98
9 changed files with 450 additions and 47 deletions

View File

@ -1,4 +1,4 @@
*eval.txt* For Vim version 7.4. Last change: 2016 Jul 24
*eval.txt* For Vim version 7.4. Last change: 2016 Jul 29
VIM REFERENCE MANUAL by Bram Moolenaar
@ -40,7 +40,7 @@ done, the features in this document are not available. See |+eval| and
There are nine types of variables:
Number A 32 or 64 bit signed number. |expr-number| *Number*
64-bit Number is available only when compiled with the
64-bit Numbers are available only when compiled with the
|+num64| feature.
Examples: -123 0x10 0177
@ -1219,7 +1219,7 @@ the following ways:
1. The body of the lambda expression is an |expr1| and not a sequence of |Ex|
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}
:echo F(5, 2)
< 3
@ -1228,6 +1228,18 @@ The arguments are optional. Example: >
:let F = {-> 'error function'}
:echo F()
< 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()|: >
:echo map([1, 2, 3], {idx, val -> val + 1})
@ -1245,6 +1257,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.
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*
@ -7494,7 +7512,8 @@ test_null_string() *test_null_string()*
test_settime({expr}) *test_settime()*
Set the time Vim uses internally. Currently only used for
timestamps in the history, as they are used in viminfo.
timestamps in the history, as they are used in viminfo, and
for undo.
{expr} must evaluate to a number. When the value is zero the
normal behavior is restored.

View File

@ -237,8 +237,8 @@ static int get_env_tv(char_u **arg, typval_T *rettv, int evaluate);
static int get_env_len(char_u **arg);
static char_u * make_expanded_name(char_u *in_start, char_u *expr_start, char_u *expr_end, char_u *in_end);
static void check_vars(char_u *name, int len);
static typval_T *alloc_string_tv(char_u *string);
static hashtab_T *find_var_ht(char_u *name, char_u **varname);
static void delete_var(hashtab_T *ht, hashitem_T *hi);
static void list_one_var(dictitem_T *v, char_u *prefix, int *first);
static void list_one_var_a(char_u *prefix, char_u *name, int type, char_u *string, int *first);
@ -4332,6 +4332,9 @@ eval7(
{
partial_T *partial;
if (!evaluate)
check_vars(s, len);
/* If "s" is the name of a variable of type VAR_FUNC
* use its contents. */
s = deref_func_name(s, &len, &partial, !evaluate);
@ -4363,7 +4366,10 @@ eval7(
else if (evaluate)
ret = get_var_tv(s, len, rettv, NULL, TRUE, FALSE);
else
{
check_vars(s, len);
ret = OK;
}
}
vim_free(alias);
}
@ -5540,6 +5546,10 @@ set_ref_in_item(
}
}
}
else if (tv->v_type == VAR_FUNC)
{
abort = set_ref_in_func(tv->vval.v_string, copyID);
}
else if (tv->v_type == VAR_PARTIAL)
{
partial_T *pt = tv->vval.v_partial;
@ -5549,6 +5559,8 @@ set_ref_in_item(
*/
if (pt != NULL)
{
abort = set_ref_in_func(pt->pt_name, copyID);
if (pt->pt_dict != NULL)
{
typval_T dtv;
@ -6790,6 +6802,34 @@ get_var_tv(
return ret;
}
/*
* Check if variable "name[len]" is a local variable or an argument.
* If so, "*eval_lavars_used" is set to TRUE.
*/
static void
check_vars(char_u *name, int len)
{
int cc;
char_u *varname;
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)
@ -7274,13 +7314,20 @@ find_var(char_u *name, hashtab_T **htp, int no_autoload)
{
char_u *varname;
hashtab_T *ht;
dictitem_T *ret = NULL;
ht = find_var_ht(name, &varname);
if (htp != NULL)
*htp = ht;
if (ht == 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);
}
/*
@ -7341,7 +7388,7 @@ find_var_in_ht(
* Return NULL if the name is not valid.
* Set "varname" to the start of name without ':'.
*/
static hashtab_T *
hashtab_T *
find_var_ht(char_u *name, char_u **varname)
{
hashitem_T *hi;
@ -7617,6 +7664,10 @@ set_var(
}
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)
&& var_check_func_name(name, v == NULL))
return;

View File

@ -1265,8 +1265,16 @@ set_ref_in_timer(int copyID)
for (timer = first_timer; timer != NULL; timer = timer->tr_next)
{
tv.v_type = VAR_PARTIAL;
tv.vval.v_partial = timer->tr_partial;
if (timer->tr_partial != NULL)
{
tv.v_type = VAR_PARTIAL;
tv.vval.v_partial = timer->tr_partial;
}
else
{
tv.v_type = VAR_FUNC;
tv.vval.v_string = timer->tr_callback;
}
abort = abort || set_ref_in_item(&tv, copyID, NULL, NULL);
}
return abort;

View File

@ -1658,6 +1658,9 @@ EXTERN time_T time_for_testing INIT(= 0);
/* Abort conversion to string after a recursion error. */
EXTERN int did_echo_string_emsg INIT(= FALSE);
/* Used for checking if local variables or arguments used in a lambda. */
EXTERN int *eval_lavars_used INIT(= NULL);
#endif
/*

View File

@ -87,6 +87,7 @@ char_u *get_tv_string_chk(typval_T *varp);
char_u *get_tv_string_buf_chk(typval_T *varp, char_u *buf);
dictitem_T *find_var(char_u *name, hashtab_T **htp, int no_autoload);
dictitem_T *find_var_in_ht(hashtab_T *ht, int htname, char_u *varname, int no_autoload);
hashtab_T *find_var_ht(char_u *name, char_u **varname);
char_u *get_var_value(char_u *name);
void new_script_vars(scid_T id);
void init_var_dict(dict_T *dict, dictitem_T *dict_var, int scope);

View File

@ -46,7 +46,9 @@ void *clear_current_funccal(void);
void restore_current_funccal(void *f);
void list_func_vars(int *first);
dict_T *get_current_funccal_dict(hashtab_T *ht);
dictitem_T *find_var_in_scoped_ht(char_u *name, char_u **varname, int no_autoload);
int set_ref_in_previous_funccal(int copyID);
int set_ref_in_call_stack(int copyID);
int set_ref_in_func_args(int copyID);
int set_ref_in_func(char_u *name, int copyID);
/* vim: set ft=c : */

View File

@ -21,7 +21,7 @@ function! Test_lambda_with_timer()
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})
let s:timer_id = timer_start(50, {-> execute("let s:n += 1 | echo s:n", "")}, {"repeat": -1})
endfunction
call s:Foo()
@ -51,3 +51,161 @@ func Test_not_lamda()
let x = {'>' : 'foo'}
call assert_equal('foo', x['>'])
endfunc
function! Test_lambda_capture_by_reference()
let v = 1
let l:F = {x -> x + v}
let v = 2
call assert_equal(12, l:F(10))
endfunction
function! Test_lambda_side_effect()
function! s:update_and_return(arr)
let a:arr[1] = 5
return a:arr
endfunction
function! s:foo(arr)
return {-> s:update_and_return(a:arr)}
endfunction
let arr = [3,2,1]
call assert_equal([3, 5, 1], s:foo(arr)())
endfunction
function! Test_lambda_refer_local_variable_from_other_scope()
function! s:foo(X)
return a:X() " refer l:x in s:bar()
endfunction
function! s:bar()
let x = 123
return s:foo({-> x})
endfunction
call assert_equal(123, s:bar())
endfunction
function! Test_lambda_do_not_share_local_variable()
function! s:define_funcs()
let l:One = {-> split(execute("let a = 'abc' | echo a"))[0]}
let l:Two = {-> exists("a") ? a : "no"}
return [l:One, l:Two]
endfunction
let l:F = s:define_funcs()
call assert_equal('no', l:F[1]())
call assert_equal('abc', l:F[0]())
call assert_equal('no', l:F[1]())
endfunction
function! Test_lambda_closure()
function! s:foo()
let x = 0
return {-> [execute("let x += 1"), x][-1]}
endfunction
let l:F = s:foo()
call test_garbagecollect_now()
call assert_equal(1, l:F())
call assert_equal(2, l:F())
call assert_equal(3, l:F())
call assert_equal(4, l:F())
endfunction
function! Test_lambda_with_a_var()
function! s:foo()
let x = 2
return {... -> a:000 + [x]}
endfunction
function! s:bar()
return s:foo()(1)
endfunction
call assert_equal([1, 2], s:bar())
endfunction
function! Test_lambda_call_lambda_from_lambda()
function! s:foo(x)
let l:F1 = {-> {-> a:x}}
return {-> l:F1()}
endfunction
let l:F = s:foo(1)
call assert_equal(1, l:F()())
endfunction
function! Test_lambda_delfunc()
function! s:gen()
let pl = l:
let l:Foo = {-> get(pl, "Foo", get(pl, "Bar", {-> 0}))}
let l:Bar = l:Foo
delfunction l:Foo
return l:Bar
endfunction
let l:F = s:gen()
call assert_fails(':call l:F()', 'E117:')
endfunction
function! Test_lambda_scope()
function! s:NewCounter()
let c = 0
return {-> [execute('let c += 1'), c][-1]}
endfunction
function! s:NewCounter2()
return {-> [execute('let c += 100'), c][-1]}
endfunction
let l:C = s:NewCounter()
let l:D = s:NewCounter2()
call assert_equal(1, l:C())
call assert_fails(':call l:D()', 'E15:') " E121: then E15:
call assert_equal(2, l:C())
endfunction
function! Test_lambda_share_scope()
function! s:New()
let c = 0
let l:Inc0 = {-> [execute('let c += 1'), c][-1]}
let l:Dec0 = {-> [execute('let c -= 1'), c][-1]}
return [l:Inc0, l:Dec0]
endfunction
let [l:Inc, l:Dec] = s:New()
call assert_equal(1, l:Inc())
call assert_equal(2, l:Inc())
call assert_equal(1, l:Dec())
endfunction
function! Test_lambda_circular_reference()
function! s:Foo()
let d = {}
let d.f = {-> d}
return d.f
endfunction
call s:Foo()
call test_garbagecollect_now()
let i = 0 | while i < 10000 | call s:Foo() | let i+= 1 | endwhile
call test_garbagecollect_now()
endfunction
function! Test_lambda_combination()
call assert_equal(2, {x -> {x -> x}}(1)(2))
call assert_equal(10, {y -> {x -> x(y)(10)}({y -> y})}({z -> z}))
call assert_equal(5.0, {x -> {y -> x / y}}(10)(2.0))
call assert_equal(6, {x -> {y -> {z -> x + y + z}}}(1)(2)(3))
call assert_equal(6, {x -> {f -> f(x)}}(3)({x -> x * 2}))
call assert_equal(6, {f -> {x -> f(x)}}({x -> x * 2})(3))
" Z combinator
let Z = {f -> {x -> f({y -> x(x)(y)})}({x -> f({y -> x(x)(y)})})}
let Fact = {f -> {x -> x == 0 ? 1 : x * f(x - 1)}}
call assert_equal(120, Z(Fact)(5))
endfunction

View File

@ -15,6 +15,8 @@
#if defined(FEAT_EVAL) || defined(PROTO)
typedef struct funccall_S funccall_T;
/*
* Structure to hold info for a user function.
*/
@ -47,6 +49,7 @@ struct ufunc
scid_T uf_script_ID; /* ID of script where function was defined,
used for s: variables */
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
start with <SNR>123_ (<SNR> is K_SPECIAL
KS_EXTRA KE_SNR) */
@ -70,8 +73,6 @@ struct ufunc
#define FIXVAR_CNT 12 /* number of fixed variables */
/* structure to hold info for a function that is currently being executed. */
typedef struct funccall_S funccall_T;
struct funccall_S
{
ufunc_T *func; /* function being called */
@ -96,6 +97,11 @@ struct funccall_S
proftime_T prof_child; /* time spent in a child */
#endif
funccall_T *caller; /* calling function or NULL */
/* for closure */
int fc_refcount;
int fc_copyID; /* for garbage collection */
garray_T fc_funcs; /* list of ufunc_T* which refer this */
};
/*
@ -259,6 +265,7 @@ get_lambda_tv(char_u **arg, typval_T *rettv, int evaluate)
{
garray_T newargs;
garray_T newlines;
garray_T *pnewargs;
ufunc_T *fp = NULL;
int varargs;
int ret;
@ -266,6 +273,8 @@ get_lambda_tv(char_u **arg, typval_T *rettv, int evaluate)
char_u *start = skipwhite(*arg + 1);
char_u *s, *e;
static int lambda_no = 0;
int *old_eval_lavars = eval_lavars_used;
int eval_lavars = FALSE;
ga_init(&newargs);
ga_init(&newlines);
@ -276,11 +285,19 @@ get_lambda_tv(char_u **arg, typval_T *rettv, int evaluate)
return NOTDONE;
/* Parse the arguments again. */
if (evaluate)
pnewargs = &newargs;
else
pnewargs = NULL;
*arg = skipwhite(*arg + 1);
ret = get_function_args(arg, '-', &newargs, &varargs, FALSE);
ret = get_function_args(arg, '-', pnewargs, &varargs, FALSE);
if (ret == FAIL || **arg != '>')
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. */
*arg = skipwhite(*arg + 1);
s = *arg;
@ -298,32 +315,42 @@ get_lambda_tv(char_u **arg, typval_T *rettv, int evaluate)
int len;
char_u *p;
fp = (ufunc_T *)alloc((unsigned)(sizeof(ufunc_T) + 20));
sprintf((char*)name, "<lambda>%d", ++lambda_no);
fp = (ufunc_T *)alloc((unsigned)(sizeof(ufunc_T) + STRLEN(name)));
if (fp == NULL)
goto errret;
sprintf((char*)name, "<lambda>%d", ++lambda_no);
ga_init2(&newlines, (int)sizeof(char_u *), 1);
if (ga_grow(&newlines, 1) == FAIL)
goto errret;
/* Add "return " before the expression.
* TODO: Support multiple expressions. */
/* Add "return " before the expression. */
len = 7 + e - s + 1;
p = (char_u *)alloc(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;
vim_strncpy(p + 7, s, e - s);
fp->uf_refcount = 1;
STRCPY(fp->uf_name, name);
hash_add(&func_hashtab, UF2HIKEY(fp));
fp->uf_args = newargs;
fp->uf_lines = newlines;
if (current_funccal != NULL && eval_lavars)
{
fp->uf_scoped = current_funccal;
current_funccal->fc_refcount++;
if (ga_grow(&current_funccal->fc_funcs, 1) == FAIL)
goto errret;
((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;
@ -341,15 +368,15 @@ get_lambda_tv(char_u **arg, typval_T *rettv, int evaluate)
rettv->vval.v_string = vim_strsave(name);
rettv->v_type = VAR_FUNC;
}
else
ga_clear_strings(&newargs);
eval_lavars_used = old_eval_lavars;
return OK;
errret:
ga_clear_strings(&newargs);
ga_clear_strings(&newlines);
vim_free(fp);
eval_lavars_used = old_eval_lavars;
return FAIL;
}
@ -624,6 +651,15 @@ free_funccal(
int free_val) /* a: vars were allocated */
{
listitem_T *li;
int i;
for (i = 0; i < fc->fc_funcs.ga_len; ++i)
{
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. */
@ -637,6 +673,16 @@ free_funccal(
for (li = fc->l_varlist.lv_first; li != NULL; li = li->li_next)
clear_tv(&li->li_tv);
for (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);
vim_free(fc);
}
@ -696,6 +742,11 @@ call_user_func(
/* Check if this function has a breakpoint. */
fc->breakpoint = dbg_find_breakpoint(FALSE, fp->uf_name, (linenr_T)0);
fc->dbg_tick = debug_tick;
/* Set up fields for closure. */
fc->fc_refcount = 0;
fc->fc_copyID = 0;
ga_init2(&fc->fc_funcs, sizeof(ufunc_T *), 1);
func_ref(fp->uf_name);
if (STRNCMP(fp->uf_name, "<lambda>", 8) == 0)
islambda = TRUE;
@ -758,7 +809,6 @@ call_user_func(
for (i = 0; i < argcount; ++i)
{
int addlocal = FALSE;
dictitem_T *v2;
ai = i - fp->uf_args.ga_len;
if (ai < 0)
@ -778,9 +828,6 @@ call_user_func(
{
v = &fc->fixvar[fixvar_idx++].var;
v->di_flags = DI_FLAGS_RO | DI_FLAGS_FIX;
if (addlocal)
v2 = v;
}
else
{
@ -789,36 +836,23 @@ call_user_func(
if (v == NULL)
break;
v->di_flags = DI_FLAGS_RO | DI_FLAGS_FIX | DI_FLAGS_ALLOC;
if (addlocal)
{
v2 = (dictitem_T *)alloc((unsigned)(sizeof(dictitem_T)
+ STRLEN(name)));
if (v2 == NULL)
{
vim_free(v);
break;
}
v2->di_flags = DI_FLAGS_RO | DI_FLAGS_FIX | DI_FLAGS_ALLOC;
}
}
STRCPY(v->di_key, name);
hash_add(&fc->l_avars.dv_hashtab, DI2HIKEY(v));
/* Note: the values are copied directly to avoid alloc/free.
* "argvars" must have VAR_FIXED for v_lock. */
v->di_tv = argvars[i];
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)
{
STRCPY(v2->di_key, name);
copy_tv(&v->di_tv, &v2->di_tv);
v2->di_tv.v_lock = VAR_FIXED;
hash_add(&fc->l_vars.dv_hashtab, DI2HIKEY(v2));
/* Named arguments should be accessed without the "a:" prefix in
* lambda expressions. Add to the l: dict. */
copy_tv(&v->di_tv, &v->di_tv);
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)
{
@ -1014,7 +1048,8 @@ call_user_func(
* free the funccall_T and what's in it. */
if (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)
&& fc->l_avars.dv_refcount == DO_NOT_FREE_CNT
&& fc->fc_refcount <= 0)
{
free_funccal(fc, FALSE);
}
@ -1048,6 +1083,52 @@ call_user_func(
}
}
/*
* Unreference "fc": decrement the reference count and free it when it
* becomes zero. If "fp" is not NULL, "fp" is detached from "fc".
*/
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;
}
}
}
}
/*
* Free a function and remove it from the list of functions.
*/
@ -1072,6 +1153,8 @@ func_free(ufunc_T *fp)
else
hash_remove(&func_hashtab, hi);
funccal_unref(fp->uf_scoped, fp);
vim_free(fp);
}
@ -2216,6 +2299,7 @@ ex_function(exarg_T *eap)
}
fp->uf_args = newargs;
fp->uf_lines = newlines;
fp->uf_scoped = NULL;
#ifdef FEAT_PROFILE
fp->uf_tml_count = NULL;
fp->uf_tml_total = NULL;
@ -2705,7 +2789,8 @@ can_free_funccal(funccall_T *fc, int copyID)
{
return (fc->l_varlist.lv_copyID != copyID
&& fc->l_vars.dv_copyID != copyID
&& fc->l_avars.dv_copyID != copyID);
&& fc->l_avars.dv_copyID != copyID
&& fc->fc_copyID != copyID);
}
/*
@ -3450,6 +3535,40 @@ get_current_funccal_dict(hashtab_T *ht)
return NULL;
}
/*
* 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;
}
/*
* Set "copyID + 1" in previous_funccal and callers.
*/
@ -3461,6 +3580,7 @@ set_ref_in_previous_funccal(int copyID)
for (fc = previous_funccal; fc != NULL; fc = fc->caller)
{
fc->fc_copyID = copyID + 1;
abort = abort || set_ref_in_ht(&fc->l_vars.dv_hashtab, copyID + 1,
NULL);
abort = abort || set_ref_in_ht(&fc->l_avars.dv_hashtab, copyID + 1,
@ -3480,6 +3600,7 @@ set_ref_in_call_stack(int copyID)
for (fc = current_funccal; fc != NULL; fc = fc->caller)
{
fc->fc_copyID = copyID;
abort = abort || set_ref_in_ht(&fc->l_vars.dv_hashtab, copyID, NULL);
abort = abort || set_ref_in_ht(&fc->l_avars.dv_hashtab, copyID, NULL);
}
@ -3501,4 +3622,42 @@ set_ref_in_func_args(int 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.
*
* Returns TRUE if setting references failed somehow.
*/
int
set_ref_in_func(char_u *name, int copyID)
{
ufunc_T *fp;
funccall_T *fc;
int error = ERROR_NONE;
char_u fname_buf[FLEN_FIXED + 1];
char_u *tofree = NULL;
char_u *fname;
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;
set_ref_in_ht(&fc->l_vars.dv_hashtab, copyID, NULL);
set_ref_in_ht(&fc->l_avars.dv_hashtab, copyID, NULL);
}
}
}
vim_free(tofree);
return FALSE;
}
#endif /* FEAT_EVAL */

View File

@ -758,6 +758,8 @@ static char *(features[]) =
static int included_patches[] =
{ /* Add new patch number below this line */
/**/
2119,
/**/
2118,
/**/