To: vim_dev@googlegroups.com Subject: Patch 7.4.2119 Fcc: outbox From: Bram Moolenaar Mime-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ------------ Patch 7.4.2119 Problem: Closures are not supported. Solution: Capture variables in lambdas from the outer scope. (Yasuhiro Matsumoto, Ken Takata) Files: runtime/doc/eval.txt, src/eval.c, src/ex_cmds2.c, src/globals.h, src/proto/eval.pro, src/proto/userfunc.pro, src/testdir/test_lambda.vim, src/userfunc.c *** ../vim-7.4.2118/runtime/doc/eval.txt 2016-07-23 15:35:14.157576957 +0200 --- runtime/doc/eval.txt 2016-07-29 22:01:19.607508286 +0200 *************** *** 1205,1216 **** {args -> expr1} lambda expression A lambda expression creates a new unnamed function which returns the result of ! evaluating |expr1|. Lambda expressions are differ from |user-functions| in 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.: > :let F = {arg1, arg2 -> arg1 - arg2} :echo F(5, 2) < 3 --- 1214,1225 ---- {args -> expr1} lambda expression A lambda expression creates a new unnamed function which returns the result of ! evaluating |expr1|. Lambda expressions differ from |user-functions| in the following ways: 1. The body of the lambda expression is an |expr1| and not a sequence of |Ex| commands. ! 2. The prefix "a:" should not be used for arguments. E.g.: > :let F = {arg1, arg2 -> arg1 - arg2} :echo F(5, 2) < 3 *************** *** 1219,1224 **** --- 1228,1245 ---- :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}) *************** *** 1236,1241 **** --- 1257,1268 ---- Note how execute() is used to execute an Ex command. That's ugly though. + + Lambda expressions have internal names like '42'. If you get an error + for a lambda expression, you can find what it is with the following command: > + :function {'42'} + See also: |numbered-function| + ============================================================================== 3. Internal variable *internal-variables* *E461* *** ../vim-7.4.2118/src/eval.c 2016-07-24 21:58:39.696057708 +0200 --- src/eval.c 2016-07-29 22:02:32.558823068 +0200 *************** *** 237,244 **** 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 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); --- 237,244 ---- 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 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,4337 **** --- 4332,4340 ---- { 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,4369 **** --- 4366,4375 ---- else if (evaluate) ret = get_var_tv(s, len, rettv, NULL, TRUE, FALSE); else + { + check_vars(s, len); ret = OK; + } } vim_free(alias); } *************** *** 5540,5545 **** --- 5546,5555 ---- } } } + 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,5554 **** --- 5559,5566 ---- */ if (pt != NULL) { + abort = set_ref_in_func(pt->pt_name, copyID); + if (pt->pt_dict != NULL) { typval_T dtv; *************** *** 6791,6796 **** --- 6803,6836 ---- } /* + * 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) * Can all be combined: dict.func(expr)[idx]['func'](expr) *************** *** 7274,7286 **** { char_u *varname; hashtab_T *ht; 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); } /* --- 7314,7333 ---- { 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; ! 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,7347 **** * Return NULL if the name is not valid. * Set "varname" to the start of name without ':'. */ ! static hashtab_T * find_var_ht(char_u *name, char_u **varname) { hashitem_T *hi; --- 7388,7394 ---- * Return NULL if the name is not valid. * Set "varname" to the start of name without ':'. */ ! hashtab_T * find_var_ht(char_u *name, char_u **varname) { hashitem_T *hi; *************** *** 7617,7622 **** --- 7664,7673 ---- } 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; *** ../vim-7.4.2118/src/ex_cmds2.c 2016-07-24 21:58:39.700057671 +0200 --- src/ex_cmds2.c 2016-07-29 21:52:07.432694039 +0200 *************** *** 1265,1272 **** for (timer = first_timer; timer != NULL; timer = timer->tr_next) { ! tv.v_type = VAR_PARTIAL; ! tv.vval.v_partial = timer->tr_partial; abort = abort || set_ref_in_item(&tv, copyID, NULL, NULL); } return abort; --- 1265,1280 ---- for (timer = first_timer; timer != NULL; timer = timer->tr_next) { ! 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; *** ../vim-7.4.2118/src/globals.h 2016-07-26 22:14:04.457444251 +0200 --- src/globals.h 2016-07-29 21:52:07.432694039 +0200 *************** *** 1658,1663 **** --- 1658,1666 ---- /* 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 /* *** ../vim-7.4.2118/src/proto/eval.pro 2016-07-23 15:35:14.157576957 +0200 --- src/proto/eval.pro 2016-07-29 21:52:07.432694039 +0200 *************** *** 87,92 **** --- 87,93 ---- 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); *** ../vim-7.4.2118/src/proto/userfunc.pro 2016-07-22 21:49:36.678031435 +0200 --- src/proto/userfunc.pro 2016-07-29 21:52:07.432694039 +0200 *************** *** 46,52 **** --- 46,54 ---- 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 : */ *** ../vim-7.4.2118/src/testdir/test_lambda.vim 2016-07-19 22:43:06.378767804 +0200 --- src/testdir/test_lambda.vim 2016-07-29 21:52:07.432694039 +0200 *************** *** 21,27 **** 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() --- 21,27 ---- 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() *************** *** 51,53 **** --- 51,211 ---- 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 *** ../vim-7.4.2118/src/userfunc.c 2016-07-26 20:46:02.862976266 +0200 --- src/userfunc.c 2016-07-29 22:06:02.936846906 +0200 *************** *** 15,20 **** --- 15,22 ---- #if defined(FEAT_EVAL) || defined(PROTO) + typedef struct funccall_S funccall_T; + /* * Structure to hold info for a user function. */ *************** *** 47,52 **** --- 49,55 ---- 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 123_ ( is K_SPECIAL KS_EXTRA KE_SNR) */ *************** *** 70,77 **** #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 */ --- 73,78 ---- *************** *** 96,101 **** --- 97,107 ---- 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,264 **** --- 265,271 ---- { garray_T newargs; garray_T newlines; + garray_T *pnewargs; ufunc_T *fp = NULL; int varargs; int ret; *************** *** 266,271 **** --- 273,280 ---- 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,286 **** return NOTDONE; /* Parse the arguments again. */ *arg = skipwhite(*arg + 1); ! ret = get_function_args(arg, '-', &newargs, &varargs, FALSE); if (ret == FAIL || **arg != '>') goto errret; /* Get the start and the end of the expression. */ *arg = skipwhite(*arg + 1); s = *arg; --- 285,303 ---- return NOTDONE; /* Parse the arguments again. */ + if (evaluate) + pnewargs = &newargs; + else + pnewargs = NULL; *arg = skipwhite(*arg + 1); ! 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,329 **** int len; char_u *p; ! fp = (ufunc_T *)alloc((unsigned)(sizeof(ufunc_T) + 20)); if (fp == NULL) goto errret; - sprintf((char*)name, "%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. */ 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; fp->uf_refcount = 1; STRCPY(fp->uf_name, name); hash_add(&func_hashtab, UF2HIKEY(fp)); fp->uf_args = newargs; fp->uf_lines = newlines; #ifdef FEAT_PROFILE fp->uf_tml_count = NULL; --- 315,356 ---- int len; char_u *p; ! sprintf((char*)name, "%d", ++lambda_no); ! ! fp = (ufunc_T *)alloc((unsigned)(sizeof(ufunc_T) + STRLEN(name))); if (fp == NULL) goto errret; ga_init2(&newlines, (int)sizeof(char_u *), 1); if (ga_grow(&newlines, 1) == FAIL) goto errret; ! /* 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 "); ! 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(¤t_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,355 **** rettv->vval.v_string = vim_strsave(name); rettv->v_type = VAR_FUNC; } - else - ga_clear_strings(&newargs); return OK; errret: ga_clear_strings(&newargs); ga_clear_strings(&newlines); vim_free(fp); return FAIL; } --- 368,382 ---- rettv->vval.v_string = vim_strsave(name); rettv->v_type = VAR_FUNC; } + 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,629 **** --- 651,665 ---- 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,642 **** --- 673,688 ---- 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,701 **** --- 742,752 ---- /* 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, "", 8) == 0) islambda = TRUE; *************** *** 758,764 **** for (i = 0; i < argcount; ++i) { int addlocal = FALSE; - dictitem_T *v2; ai = i - fp->uf_args.ga_len; if (ai < 0) --- 809,814 ---- *************** *** 778,786 **** { v = &fc->fixvar[fixvar_idx++].var; v->di_flags = DI_FLAGS_RO | DI_FLAGS_FIX; - - if (addlocal) - v2 = v; } else { --- 828,833 ---- *************** *** 789,824 **** 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)); } if (ai >= 0 && ai < MAX_FUNC_ARGS) { --- 836,858 ---- if (v == NULL) break; v->di_flags = DI_FLAGS_RO | DI_FLAGS_FIX | DI_FLAGS_ALLOC; } STRCPY(v->di_key, name); /* 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; if (addlocal) { ! /* 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,1020 **** * 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) { free_funccal(fc, FALSE); } --- 1048,1055 ---- * 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->fc_refcount <= 0) { free_funccal(fc, FALSE); } *************** *** 1049,1054 **** --- 1084,1135 ---- } /* + * 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. */ static void *************** *** 1072,1077 **** --- 1153,1160 ---- else hash_remove(&func_hashtab, hi); + funccal_unref(fp->uf_scoped, fp); + vim_free(fp); } *************** *** 2216,2221 **** --- 2299,2305 ---- } 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,2711 **** { return (fc->l_varlist.lv_copyID != copyID && fc->l_vars.dv_copyID != copyID ! && fc->l_avars.dv_copyID != copyID); } /* --- 2789,2796 ---- { return (fc->l_varlist.lv_copyID != copyID && fc->l_vars.dv_copyID != copyID ! && fc->l_avars.dv_copyID != copyID ! && fc->fc_copyID != copyID); } /* *************** *** 3451,3456 **** --- 3536,3575 ---- } /* + * 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. */ int *************** *** 3461,3466 **** --- 3580,3586 ---- 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,3485 **** --- 3600,3606 ---- 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,3504 **** --- 3622,3663 ---- 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 */ *** ../vim-7.4.2118/src/version.c 2016-07-29 21:01:06.933549301 +0200 --- src/version.c 2016-07-29 22:10:28.570351468 +0200 *************** *** 760,761 **** --- 760,763 ---- { /* Add new patch number below this line */ + /**/ + 2119, /**/ -- Close your shells, or I'll kill -9 you Tomorrow I'll quota you Remember the disks'll always be full And then while I'm away I'll write ~ everyday And I'll send-pr all my buggings to you. [ CVS log "Beatles style" for FreeBSD ports/INDEX, Satoshi Asami ] /// Bram Moolenaar -- Bram@Moolenaar.net -- http://www.Moolenaar.net \\\ /// sponsor Vim, vote for features -- http://www.Vim.org/sponsor/ \\\ \\\ an exciting new programming language -- http://www.Zimbu.org /// \\\ help me help AIDS victims -- http://ICCF-Holland.org ///