diff options
-rw-r--r-- | runtime/doc/eval.txt | 29 | ||||
-rw-r--r-- | src/channel.c | 25 | ||||
-rw-r--r-- | src/eval.c | 34 | ||||
-rw-r--r-- | src/evalfunc.c | 107 | ||||
-rw-r--r-- | src/if_mzsch.c | 2 | ||||
-rw-r--r-- | src/if_py_both.h | 2 | ||||
-rw-r--r-- | src/misc2.c | 12 | ||||
-rw-r--r-- | src/proto/eval.pro | 1 | ||||
-rw-r--r-- | src/proto/userfunc.pro | 6 | ||||
-rw-r--r-- | src/regexp.c | 2 | ||||
-rw-r--r-- | src/structs.h | 92 | ||||
-rw-r--r-- | src/testdir/test_expr.vim | 15 | ||||
-rw-r--r-- | src/testdir/test_lambda.vim | 2 | ||||
-rw-r--r-- | src/userfunc.c | 346 | ||||
-rw-r--r-- | src/version.c | 2 | ||||
-rw-r--r-- | src/vim.h | 1 |
16 files changed, 447 insertions, 231 deletions
diff --git a/runtime/doc/eval.txt b/runtime/doc/eval.txt index 8dfbb3a28e..7e0b73c612 100644 --- a/runtime/doc/eval.txt +++ b/runtime/doc/eval.txt @@ -1,4 +1,4 @@ -*eval.txt* For Vim version 7.4. Last change: 2016 Jul 29 +*eval.txt* For Vim version 7.4. Last change: 2016 Jul 31 VIM REFERENCE MANUAL by Bram Moolenaar @@ -1240,7 +1240,9 @@ function returns: > :let Bar = Foo(4) :echo Bar(6) < 5 -See also |:func-closure|. + +See also |:func-closure|. Lambda and closure support can be checked with: > + if has('lambda') Examples for using a lambda expression with |sort()|, |map()| and |filter()|: > :echo map([1, 2, 3], {idx, val -> val + 1}) @@ -2071,8 +2073,10 @@ foldlevel({lnum}) Number fold level at {lnum} foldtext() String line displayed for closed fold foldtextresult({lnum}) String text for closed fold at {lnum} foreground() Number bring the Vim window to the foreground -function({name} [, {arglist}] [, {dict}]) +funcref({name} [, {arglist}] [, {dict}]) Funcref reference to function {name} +function({name} [, {arglist}] [, {dict}]) + Funcref named reference to function {name} garbagecollect([{atexit}]) none free memory, breaking cyclic references get({list}, {idx} [, {def}]) any get item {idx} from {list} or {def} get({dict}, {key} [, {def}]) any get item {key} from {dict} or {def} @@ -3850,6 +3854,15 @@ foreground() Move the Vim window to the foreground. Useful when sent from {only in the Win32, Athena, Motif and GTK GUI versions and the Win32 console version} + *funcref()* +funcref({name} [, {arglist}] [, {dict}]) + Just like |function()|, but the returned Funcref will lookup + the function by reference, not by name. This matters when the + function {name} is redefined later. + + Unlike |function()|, {name} must be an existing user function. + Also for autoloaded functions. {name} cannot be a builtin + function. *function()* *E700* *E922* *E923* function({name} [, {arglist}] [, {dict}]) @@ -3857,12 +3870,16 @@ function({name} [, {arglist}] [, {dict}]) {name} can be the name of a user defined function or an internal function. - {name} can also be a Funcref, also a partial. When it is a + {name} can also be a Funcref or a partial. When it is a partial the dict stored in it will be used and the {dict} argument is not allowed. E.g.: > let FuncWithArg = function(dict.Func, [arg]) let Broken = function(dict.Func, [arg], dict) < + When using the Funcref the function will be found by {name}, + also when it was redefined later. Use |funcref()| to keep the + same function. + When {arglist} or {dict} is present this creates a partial. That means the argument list and/or the dictionary is stored in the Funcref and will be used when the Funcref is called. @@ -6191,6 +6208,7 @@ screenrow() *screenrow()* The result is a Number, which is the current screen row of the cursor. The top line has number one. This function is mainly used for testing. + Alternatively you can use |winline()|. Note: Same restrictions as with |screencol()|. @@ -8039,6 +8057,7 @@ insert_expand Compiled with support for CTRL-X expansion commands in Insert mode. jumplist Compiled with |jumplist| support. keymap Compiled with 'keymap' support. +lambda Compiled with |lambda| support. langmap Compiled with 'langmap' support. libcall Compiled with |libcall()| support. linebreak Compiled with 'linebreak', 'breakat', 'showbreak' and @@ -8294,7 +8313,7 @@ See |:verbose-cmd| for more information. :endf[unction] The end of a function definition. Must be on a line by its own, without other commands. - *:delf* *:delfunction* *E130* *E131* + *:delf* *:delfunction* *E130* *E131* *E933* :delf[unction] {name} Delete function {name}. {name} can also be a |Dictionary| entry that is a |Funcref|: > diff --git a/src/channel.c b/src/channel.c index 3b8b5afa05..5ee05e3332 100644 --- a/src/channel.c +++ b/src/channel.c @@ -1124,15 +1124,18 @@ set_callback( if (callback != NULL && *callback != NUL) { if (partial != NULL) - *cbp = partial->pt_name; + *cbp = partial_name(partial); else + { *cbp = vim_strsave(callback); + func_ref(*cbp); + } } else *cbp = NULL; *pp = partial; - if (*pp != NULL) - ++(*pp)->pt_refcount; + if (partial != NULL) + ++partial->pt_refcount; } /* @@ -1279,7 +1282,10 @@ channel_set_req_callback( item->cq_callback = callback; } else + { item->cq_callback = vim_strsave(callback); + func_ref(item->cq_callback); + } item->cq_seq_nr = id; item->cq_prev = head->cq_prev; head->cq_prev = item; @@ -3923,14 +3929,24 @@ free_job_options(jobopt_T *opt) { if (opt->jo_partial != NULL) partial_unref(opt->jo_partial); + else if (opt->jo_callback != NULL) + func_unref(opt->jo_callback); if (opt->jo_out_partial != NULL) partial_unref(opt->jo_out_partial); + else if (opt->jo_out_cb != NULL) + func_unref(opt->jo_out_cb); if (opt->jo_err_partial != NULL) partial_unref(opt->jo_err_partial); + else if (opt->jo_err_cb != NULL) + func_unref(opt->jo_err_cb); if (opt->jo_close_partial != NULL) partial_unref(opt->jo_close_partial); + else if (opt->jo_close_cb != NULL) + func_unref(opt->jo_close_cb); if (opt->jo_exit_partial != NULL) partial_unref(opt->jo_exit_partial); + else if (opt->jo_exit_cb != NULL) + func_unref(opt->jo_exit_cb); } /* @@ -4476,7 +4492,10 @@ job_set_options(job_T *job, jobopt_T *opt) ++job->jv_exit_partial->pt_refcount; } else + { job->jv_exit_cb = vim_strsave(opt->jo_exit_cb); + func_ref(job->jv_exit_cb); + } } } } diff --git a/src/eval.c b/src/eval.c index cf2c771438..495bbb0697 100644 --- a/src/eval.c +++ b/src/eval.c @@ -5011,6 +5011,17 @@ get_lit_string_tv(char_u **arg, typval_T *rettv, int evaluate) return OK; } +/* + * Return the function name of the partial. + */ + char_u * +partial_name(partial_T *pt) +{ + if (pt->pt_name != NULL) + return pt->pt_name; + return pt->pt_func->uf_name; +} + static void partial_free(partial_T *pt) { @@ -5020,8 +5031,13 @@ partial_free(partial_T *pt) clear_tv(&pt->pt_argv[i]); vim_free(pt->pt_argv); dict_unref(pt->pt_dict); - func_unref(pt->pt_name); - vim_free(pt->pt_name); + if (pt->pt_name != NULL) + { + func_unref(pt->pt_name); + vim_free(pt->pt_name); + } + else + func_ptr_unref(pt->pt_func); vim_free(pt); } @@ -5051,11 +5067,11 @@ func_equal( /* empty and NULL function name considered the same */ s1 = tv1->v_type == VAR_FUNC ? tv1->vval.v_string - : tv1->vval.v_partial->pt_name; + : partial_name(tv1->vval.v_partial); if (s1 != NULL && *s1 == NUL) s1 = NULL; s2 = tv2->v_type == VAR_FUNC ? tv2->vval.v_string - : tv2->vval.v_partial->pt_name; + : partial_name(tv2->vval.v_partial); if (s2 != NULL && *s2 == NUL) s2 = NULL; if (s1 == NULL || s2 == NULL) @@ -5550,7 +5566,7 @@ set_ref_in_item( } else if (tv->v_type == VAR_FUNC) { - abort = set_ref_in_func(tv->vval.v_string, copyID); + abort = set_ref_in_func(tv->vval.v_string, NULL, copyID); } else if (tv->v_type == VAR_PARTIAL) { @@ -5561,7 +5577,7 @@ set_ref_in_item( */ if (pt != NULL) { - abort = set_ref_in_func(pt->pt_name, copyID); + abort = set_ref_in_func(pt->pt_name, pt->pt_func, copyID); if (pt->pt_dict != NULL) { @@ -5735,7 +5751,7 @@ echo_string_core( { partial_T *pt = tv->vval.v_partial; char_u *fname = string_quote(pt == NULL ? NULL - : pt->pt_name, FALSE); + : partial_name(pt), FALSE); garray_T ga; int i; char_u *tf; @@ -6871,7 +6887,7 @@ handle_subscript( if (functv.v_type == VAR_PARTIAL) { pt = functv.vval.v_partial; - s = pt->pt_name; + s = partial_name(pt); } else s = functv.vval.v_string; @@ -10025,7 +10041,7 @@ filter_map_one(typval_T *tv, typval_T *expr, int map, int *remp) { partial_T *partial = expr->vval.v_partial; - s = partial->pt_name; + s = partial_name(partial); if (call_func(s, (int)STRLEN(s), &rettv, 2, argv, NULL, 0L, 0L, &dummy, TRUE, partial, NULL) == FAIL) goto theend; diff --git a/src/evalfunc.c b/src/evalfunc.c index 13864ea3a7..68a8b16414 100644 --- a/src/evalfunc.c +++ b/src/evalfunc.c @@ -148,6 +148,7 @@ static void f_foldlevel(typval_T *argvars, typval_T *rettv); static void f_foldtext(typval_T *argvars, typval_T *rettv); static void f_foldtextresult(typval_T *argvars, typval_T *rettv); static void f_foreground(typval_T *argvars, typval_T *rettv); +static void f_funcref(typval_T *argvars, typval_T *rettv); static void f_function(typval_T *argvars, typval_T *rettv); static void f_garbagecollect(typval_T *argvars, typval_T *rettv); static void f_get(typval_T *argvars, typval_T *rettv); @@ -563,6 +564,7 @@ static struct fst {"foldtext", 0, 0, f_foldtext}, {"foldtextresult", 1, 1, f_foldtextresult}, {"foreground", 0, 0, f_foreground}, + {"funcref", 1, 3, f_funcref}, {"function", 1, 3, f_function}, {"garbagecollect", 0, 1, f_garbagecollect}, {"get", 2, 3, f_get}, @@ -1723,7 +1725,7 @@ f_call(typval_T *argvars, typval_T *rettv) else if (argvars[0].v_type == VAR_PARTIAL) { partial = argvars[0].vval.v_partial; - func = partial->pt_name; + func = partial_name(partial); } else func = get_tv_string(&argvars[0]); @@ -3543,16 +3545,14 @@ f_foreground(typval_T *argvars UNUSED, typval_T *rettv UNUSED) #endif } -/* - * "function()" function - */ static void -f_function(typval_T *argvars, typval_T *rettv) +common_function(typval_T *argvars, typval_T *rettv, int is_funcref) { char_u *s; char_u *name; int use_string = FALSE; partial_T *arg_pt = NULL; + char_u *trans_name = NULL; if (argvars[0].v_type == VAR_FUNC) { @@ -3564,7 +3564,7 @@ f_function(typval_T *argvars, typval_T *rettv) { /* function(dict.MyFunc, [arg]) */ arg_pt = argvars[0].vval.v_partial; - s = arg_pt->pt_name; + s = partial_name(arg_pt); } else { @@ -3573,11 +3573,22 @@ f_function(typval_T *argvars, typval_T *rettv) use_string = TRUE; } + if (((use_string && vim_strchr(s, AUTOLOAD_CHAR) == NULL) + || is_funcref)) + { + name = s; + trans_name = trans_function_name(&name, FALSE, + TFN_INT | TFN_QUIET | TFN_NO_AUTOLOAD | TFN_NO_DEREF, NULL, NULL); + if (*name != NUL) + s = NULL; + } + if (s == NULL || *s == NUL || (use_string && VIM_ISDIGIT(*s))) EMSG2(_(e_invarg2), s); /* Don't check an autoload name for existence here. */ - else if (use_string && vim_strchr(s, AUTOLOAD_CHAR) == NULL - && !function_exists(s, TRUE)) + else if (trans_name != NULL && (is_funcref + ? find_func(trans_name) == NULL + : !translated_function_exists(trans_name))) EMSG2(_("E700: Unknown function: %s"), s); else { @@ -3625,7 +3636,7 @@ f_function(typval_T *argvars, typval_T *rettv) { EMSG(_("E922: expected a dict")); vim_free(name); - return; + goto theend; } if (argvars[dict_idx].vval.v_dict == NULL) dict_idx = 0; @@ -3636,14 +3647,14 @@ f_function(typval_T *argvars, typval_T *rettv) { EMSG(_("E923: Second argument of function() must be a list or a dict")); vim_free(name); - return; + goto theend; } list = argvars[arg_idx].vval.v_list; if (list == NULL || list->lv_len == 0) arg_idx = 0; } } - if (dict_idx > 0 || arg_idx > 0 || arg_pt != NULL) + if (dict_idx > 0 || arg_idx > 0 || arg_pt != NULL || is_funcref) { partial_T *pt = (partial_T *)alloc_clear(sizeof(partial_T)); @@ -3670,17 +3681,14 @@ f_function(typval_T *argvars, typval_T *rettv) { vim_free(pt); vim_free(name); - return; - } - else - { - for (i = 0; i < arg_len; i++) - copy_tv(&arg_pt->pt_argv[i], &pt->pt_argv[i]); - if (lv_len > 0) - for (li = list->lv_first; li != NULL; - li = li->li_next) - copy_tv(&li->li_tv, &pt->pt_argv[i++]); + goto theend; } + for (i = 0; i < arg_len; i++) + copy_tv(&arg_pt->pt_argv[i], &pt->pt_argv[i]); + if (lv_len > 0) + for (li = list->lv_first; li != NULL; + li = li->li_next) + copy_tv(&li->li_tv, &pt->pt_argv[i++]); } /* For "function(dict.func, [], dict)" and "func" is a partial @@ -3702,8 +3710,23 @@ f_function(typval_T *argvars, typval_T *rettv) } pt->pt_refcount = 1; - pt->pt_name = name; - func_ref(pt->pt_name); + if (arg_pt != NULL && arg_pt->pt_func != NULL) + { + pt->pt_func = arg_pt->pt_func; + func_ptr_ref(pt->pt_func); + vim_free(name); + } + else if (is_funcref) + { + pt->pt_func = find_func(trans_name); + func_ptr_ref(pt->pt_func); + vim_free(name); + } + else + { + pt->pt_name = name; + func_ref(name); + } } rettv->v_type = VAR_PARTIAL; rettv->vval.v_partial = pt; @@ -3716,6 +3739,26 @@ f_function(typval_T *argvars, typval_T *rettv) func_ref(name); } } +theend: + vim_free(trans_name); +} + +/* + * "funcref()" function + */ + static void +f_funcref(typval_T *argvars, typval_T *rettv) +{ + common_function(argvars, rettv, TRUE); +} + +/* + * "function()" function + */ + static void +f_function(typval_T *argvars, typval_T *rettv) +{ + common_function(argvars, rettv, FALSE); } /* @@ -3781,14 +3824,20 @@ f_get(typval_T *argvars, typval_T *rettv) if (pt != NULL) { char_u *what = get_tv_string(&argvars[1]); + char_u *n; if (STRCMP(what, "func") == 0 || STRCMP(what, "name") == 0) { rettv->v_type = (*what == 'f' ? VAR_FUNC : VAR_STRING); - if (pt->pt_name == NULL) + n = partial_name(pt); + if (n == NULL) rettv->vval.v_string = NULL; else - rettv->vval.v_string = vim_strsave(pt->pt_name); + { + rettv->vval.v_string = vim_strsave(n); + if (rettv->v_type == VAR_FUNC) + func_ref(rettv->vval.v_string); + } } else if (STRCMP(what, "dict") == 0) { @@ -10104,7 +10153,7 @@ item_compare2(const void *s1, const void *s2) if (partial == NULL) func_name = sortinfo->item_compare_func; else - func_name = partial->pt_name; + func_name = partial_name(partial); /* Copy the values. This is needed to be able to set v_lock to VAR_FIXED * in the copy without changing the original list items. */ @@ -11863,16 +11912,14 @@ get_callback(typval_T *arg, partial_T **pp) { *pp = arg->vval.v_partial; ++(*pp)->pt_refcount; - return (*pp)->pt_name; + return partial_name(*pp); } *pp = NULL; - if (arg->v_type == VAR_FUNC) + if (arg->v_type == VAR_FUNC || arg->v_type == VAR_STRING) { func_ref(arg->vval.v_string); return arg->vval.v_string; } - if (arg->v_type == VAR_STRING) - return arg->vval.v_string; if (arg->v_type == VAR_NUMBER && arg->vval.v_number == 0) return (char_u *)""; EMSG(_("E921: Invalid callback argument")); diff --git a/src/if_mzsch.c b/src/if_mzsch.c index daec6b0a82..7a462ad54b 100644 --- a/src/if_mzsch.c +++ b/src/if_mzsch.c @@ -3134,7 +3134,7 @@ vim_to_mzscheme_impl(typval_T *vim_value, int depth, Scheme_Hash_Table *visited) /* FIXME: func_ref() and func_unref() are needed. */ /* TODO: Support pt_dict and pt_argv. */ funcname = scheme_make_byte_string( - (char *)vim_value->vval.v_partial->pt_name); + (char *)partial_name(vim_value->vval.v_partial)); MZ_GC_CHECK(); result = scheme_make_closed_prim_w_arity(vim_funcref, funcname, (const char *)BYTE_STRING_VALUE(funcname), 0, -1); diff --git a/src/if_py_both.h b/src/if_py_both.h index 062f8226d7..4f916b7975 100644 --- a/src/if_py_both.h +++ b/src/if_py_both.h @@ -6310,7 +6310,7 @@ ConvertToPyObject(typval_T *tv) if (tv->vval.v_partial->pt_dict != NULL) tv->vval.v_partial->pt_dict->dv_refcount++; return NEW_FUNCTION(tv->vval.v_partial == NULL - ? (char_u *)"" : tv->vval.v_partial->pt_name, + ? (char_u *)"" : partial_name(tv->vval.v_partial), tv->vval.v_partial->pt_argc, argv, tv->vval.v_partial->pt_dict, tv->vval.v_partial->pt_auto); diff --git a/src/misc2.c b/src/misc2.c index 53469a261f..f44c33cea2 100644 --- a/src/misc2.c +++ b/src/misc2.c @@ -1217,16 +1217,20 @@ free_all_mem(void) if (delete_first_msg() == FAIL) break; -# ifdef FEAT_EVAL - eval_clear(); -# endif # ifdef FEAT_JOB_CHANNEL channel_free_all(); - job_free_all(); # endif #ifdef FEAT_TIMERS timer_free_all(); #endif +# ifdef FEAT_EVAL + /* must be after channel_free_all() with unrefs partials */ + eval_clear(); +# endif +# ifdef FEAT_JOB_CHANNEL + /* must be after eval_clear() with unrefs jobs */ + job_free_all(); +# endif free_termoptions(); diff --git a/src/proto/eval.pro b/src/proto/eval.pro index 56f0b49278..7a24683802 100644 --- a/src/proto/eval.pro +++ b/src/proto/eval.pro @@ -40,6 +40,7 @@ char_u *get_user_var_name(expand_T *xp, int idx); int eval0(char_u *arg, typval_T *rettv, char_u **nextcmd, int evaluate); int eval1(char_u **arg, typval_T *rettv, int evaluate); int get_option_tv(char_u **arg, typval_T *rettv, int evaluate); +char_u *partial_name(partial_T *pt); void partial_unref(partial_T *pt); int tv_equal(typval_T *tv1, typval_T *tv2, int ic, int recursive); int get_copyID(void); diff --git a/src/proto/userfunc.pro b/src/proto/userfunc.pro index bd39604478..3988683cc5 100644 --- a/src/proto/userfunc.pro +++ b/src/proto/userfunc.pro @@ -3,9 +3,11 @@ void func_init(void); int get_lambda_tv(char_u **arg, typval_T *rettv, int evaluate); char_u *deref_func_name(char_u *name, int *lenp, partial_T **partialp, int no_autoload); int get_func_tv(char_u *name, int len, typval_T *rettv, char_u **arg, linenr_T firstline, linenr_T lastline, int *doesrange, int evaluate, partial_T *partial, dict_T *selfdict); +ufunc_T *find_func(char_u *name); void free_all_functions(void); int func_call(char_u *name, typval_T *args, partial_T *partial, dict_T *selfdict, typval_T *rettv); int call_func(char_u *funcname, int len, typval_T *rettv, int argcount_in, typval_T *argvars_in, int (*argv_func)(int, typval_T *, int), linenr_T firstline, linenr_T lastline, int *doesrange, int evaluate, partial_T *partial, dict_T *selfdict_in); +char_u *trans_function_name(char_u **pp, int skip, int flags, funcdict_T *fdp, partial_T **partial); void ex_function(exarg_T *eap); int eval_fname_script(char_u *p); int translated_function_exists(char_u *name); @@ -17,7 +19,9 @@ void prof_child_exit(proftime_T *tm); char_u *get_user_func_name(expand_T *xp, int idx); void ex_delfunction(exarg_T *eap); void func_unref(char_u *name); +void func_ptr_unref(ufunc_T *fp); void func_ref(char_u *name); +void func_ptr_ref(ufunc_T *fp); void ex_return(exarg_T *eap); void ex_call(exarg_T *eap); int do_return(exarg_T *eap, int reanimate, int is_cmd, void *rettv); @@ -51,5 +55,5 @@ dictitem_T *find_var_in_scoped_ht(char_u *name, char_u **varname, int no_autoloa 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); +int set_ref_in_func(char_u *name, ufunc_T *fp_in, int copyID); /* vim: set ft=c : */ diff --git a/src/regexp.c b/src/regexp.c index 6b23cfbec9..6a685e30b3 100644 --- a/src/regexp.c +++ b/src/regexp.c @@ -7499,7 +7499,7 @@ vim_regsub_both( { partial_T *partial = expr->vval.v_partial; - s = partial->pt_name; + s = partial_name(partial); call_func(s, (int)STRLEN(s), &rettv, 1, argv, fill_submatch_list, 0L, 0L, &dummy, TRUE, partial, NULL); diff --git a/src/structs.h b/src/structs.h index 7375c7c2b8..11f182a77a 100644 --- a/src/structs.h +++ b/src/structs.h @@ -1295,10 +1295,100 @@ struct dictvar_S dict_T *dv_used_prev; /* previous dict in used dicts list */ }; +#if defined(FEAT_EVAL) || defined(PROTO) +typedef struct funccall_S funccall_T; + +/* + * Structure to hold info for a user function. + */ +typedef struct +{ + int uf_varargs; /* variable nr of arguments */ + int uf_flags; + int uf_calls; /* nr of active calls */ + garray_T uf_args; /* arguments */ + garray_T uf_lines; /* function lines */ +#ifdef FEAT_PROFILE + int uf_profiling; /* TRUE when func is being profiled */ + /* profiling the function as a whole */ + int uf_tm_count; /* nr of calls */ + proftime_T uf_tm_total; /* time spent in function + children */ + proftime_T uf_tm_self; /* time spent in function itself */ + proftime_T uf_tm_children; /* time spent in children this call */ + /* profiling the function per line */ + int *uf_tml_count; /* nr of times line was executed */ + proftime_T *uf_tml_total; /* time spent in a line + children */ + proftime_T *uf_tml_self; /* time spent in a line itself */ + proftime_T uf_tml_start; /* start time for current line */ + proftime_T uf_tml_children; /* time spent in children for this line */ + proftime_T uf_tml_wait; /* start wait time for current line */ + int uf_tml_idx; /* index of line being timed; -1 if none */ + int uf_tml_execed; /* line being timed was executed */ +#endif + 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) */ +} ufunc_T; + +#define MAX_FUNC_ARGS 20 /* maximum number of function arguments */ +#define VAR_SHORT_LEN 20 /* short variable name length */ +#define FIXVAR_CNT 12 /* number of fixed variables */ + +/* structure to hold info for a function that is currently being executed. */ +struct funccall_S +{ + ufunc_T *func; /* function being called */ + int linenr; /* next line to be executed */ + int returned; /* ":return" used */ + struct /* fixed variables for arguments */ + { + dictitem_T var; /* variable (without room for name) */ + char_u room[VAR_SHORT_LEN]; /* room for the name */ + } fixvar[FIXVAR_CNT]; + dict_T l_vars; /* l: local function variables */ + dictitem_T l_vars_var; /* variable for l: scope */ + dict_T l_avars; /* a: argument variables */ + dictitem_T l_avars_var; /* variable for a: scope */ + list_T l_varlist; /* list for a:000 */ + listitem_T l_listitems[MAX_FUNC_ARGS]; /* listitems for a:000 */ + typval_T *rettv; /* return value */ + linenr_T breakpoint; /* next line with breakpoint or zero */ + int dbg_tick; /* debug_tick when breakpoint was set */ + int level; /* top nesting level of executed function */ +#ifdef FEAT_PROFILE + 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 */ +}; + +/* + * Struct used by trans_function_name() + */ +typedef struct +{ + dict_T *fd_dict; /* Dictionary used */ + char_u *fd_newkey; /* new key in "dict" in allocated memory */ + dictitem_T *fd_di; /* Dictionary item used */ +} funcdict_T; + +#endif + struct partial_S { int pt_refcount; /* reference count */ - char_u *pt_name; /* function name */ + char_u *pt_name; /* function name; when NULL use + * pt_func->uf_name */ + ufunc_T *pt_func; /* function pointer; when NULL lookup function + * with pt_name */ int pt_auto; /* when TRUE the partial was created for using dict.member in handle_subscript() */ int pt_argc; /* number of arguments */ diff --git a/src/testdir/test_expr.vim b/src/testdir/test_expr.vim index d8abc27523..0800039aee 100644 --- a/src/testdir/test_expr.vim +++ b/src/testdir/test_expr.vim @@ -179,3 +179,18 @@ func Test_function_with_funcref() call assert_equal(v:t_string, s:fref('x')) call assert_fails("call function('s:f')", 'E700:') endfunc + +func Test_funcref() + func! One() + return 1 + endfunc + let OneByName = function('One') + let OneByRef = funcref('One') + func! One() + return 2 + endfunc + call assert_equal(2, OneByName()) + call assert_equal(1, OneByRef()) + let OneByRef = funcref('One') + call assert_equal(2, OneByRef()) +endfunc diff --git a/src/testdir/test_lambda.vim b/src/testdir/test_lambda.vim index 4d895460e5..721c47de11 100644 --- a/src/testdir/test_lambda.vim +++ b/src/testdir/test_lambda.vim @@ -152,7 +152,7 @@ function! Test_lambda_delfunc() endfunction let l:F = s:gen() - call assert_fails(':call l:F()', 'E117:') + call assert_fails(':call l:F()', 'E933:') endfunction function! Test_lambda_scope() diff --git a/src/userfunc.c b/src/userfunc.c index d264e5bbbb..a9dc4e843d 100644 --- a/src/userfunc.c +++ b/src/userfunc.c @@ -14,52 +14,12 @@ #include "vim.h" #if defined(FEAT_EVAL) || defined(PROTO) - -typedef struct funccall_S funccall_T; - -/* - * Structure to hold info for a user function. - */ -typedef struct ufunc ufunc_T; - -struct ufunc -{ - int uf_varargs; /* variable nr of arguments */ - int uf_flags; - int uf_calls; /* nr of active calls */ - garray_T uf_args; /* arguments */ - garray_T uf_lines; /* function lines */ -#ifdef FEAT_PROFILE - int uf_profiling; /* TRUE when func is being profiled */ - /* profiling the function as a whole */ - int uf_tm_count; /* nr of calls */ - proftime_T uf_tm_total; /* time spent in function + children */ - proftime_T uf_tm_self; /* time spent in function itself */ - proftime_T uf_tm_children; /* time spent in children this call */ - /* profiling the function per line */ - int *uf_tml_count; /* nr of times line was executed */ - proftime_T *uf_tml_total; /* time spent in a line + children */ - proftime_T *uf_tml_self; /* time spent in a line itself */ - proftime_T uf_tml_start; /* start time for current line */ - proftime_T uf_tml_children; /* time spent in children for this line */ - proftime_T uf_tml_wait; /* start wait time for current line */ - int uf_tml_idx; /* index of line being timed; -1 if none */ - int uf_tml_execed; /* line being timed was executed */ -#endif - 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) */ -}; - /* function flags */ #define FC_ABORT 1 /* abort function on error */ #define FC_RANGE 2 /* function accepts range */ #define FC_DICT 4 /* Dict function, uses "self" */ #define FC_CLOSURE 8 /* closure, uses outer scope variables */ +#define FC_DELETED 16 /* :delfunction used while uf_refcount > 0 */ /* From user function to hashitem and back. */ #define UF2HIKEY(fp) ((fp)->uf_name) @@ -69,52 +29,6 @@ struct ufunc #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 MAX_FUNC_ARGS 20 /* maximum number of function arguments */ -#define VAR_SHORT_LEN 20 /* short variable name length */ -#define FIXVAR_CNT 12 /* number of fixed variables */ - -/* structure to hold info for a function that is currently being executed. */ -struct funccall_S -{ - ufunc_T *func; /* function being called */ - int linenr; /* next line to be executed */ - int returned; /* ":return" used */ - struct /* fixed variables for arguments */ - { - dictitem_T var; /* variable (without room for name) */ - char_u room[VAR_SHORT_LEN]; /* room for the name */ - } fixvar[FIXVAR_CNT]; - dict_T l_vars; /* l: local function variables */ - dictitem_T l_vars_var; /* variable for l: scope */ - dict_T l_avars; /* a: argument variables */ - dictitem_T l_avars_var; /* variable for a: scope */ - list_T l_varlist; /* list for a:000 */ - listitem_T l_listitems[MAX_FUNC_ARGS]; /* listitems for a:000 */ - typval_T *rettv; /* return value */ - linenr_T breakpoint; /* next line with breakpoint or zero */ - int dbg_tick; /* debug_tick when breakpoint was set */ - int level; /* top nesting level of executed function */ -#ifdef FEAT_PROFILE - 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 */ -}; - -/* - * Struct used by trans_function_name() - */ -typedef struct -{ - dict_T *fd_dict; /* Dictionary used */ - char_u *fd_newkey; /* new key in "dict" in allocated memory */ - dictitem_T *fd_di; /* Dictionary item used */ -} funcdict_T; - /* * All user-defined functions are found in this hashtable. */ @@ -271,7 +185,7 @@ register_closure(ufunc_T *fp) return FAIL; ((ufunc_T **)current_funccal->fc_funcs.ga_data) [current_funccal->fc_funcs.ga_len++] = fp; - func_ref(current_funccal->func->uf_name); + func_ptr_ref(current_funccal->func); return OK; } @@ -288,7 +202,6 @@ get_lambda_tv(char_u **arg, typval_T *rettv, int evaluate) ufunc_T *fp = NULL; int varargs; int ret; - char_u name[20]; char_u *start = skipwhite(*arg + 1); char_u *s, *e; static int lambda_no = 0; @@ -331,14 +244,22 @@ get_lambda_tv(char_u **arg, typval_T *rettv, int evaluate) if (evaluate) { - int len, flags = 0; - char_u *p; + int len, flags = 0; + char_u *p; + char_u name[20]; + partial_T *pt; sprintf((char*)name, "<lambda>%d", ++lambda_no); fp = (ufunc_T *)alloc_clear((unsigned)(sizeof(ufunc_T) + STRLEN(name))); if (fp == NULL) goto errret; + pt = (partial_T *)alloc_clear((unsigned)sizeof(partial_T)); + if (pt == NULL) + { + vim_free(fp); + goto errret; + } ga_init2(&newlines, (int)sizeof(char_u *), 1); if (ga_grow(&newlines, 1) == FAIL) @@ -380,8 +301,10 @@ get_lambda_tv(char_u **arg, typval_T *rettv, int evaluate) fp->uf_calls = 0; fp->uf_script_ID = current_SID; - rettv->vval.v_string = vim_strsave(name); - rettv->v_type = VAR_FUNC; + pt->pt_func = fp; + pt->pt_refcount = 1; + rettv->vval.v_partial = pt; + rettv->v_type = VAR_PARTIAL; } eval_lavars_used = old_eval_lavars; @@ -406,6 +329,7 @@ deref_func_name(char_u *name, int *lenp, partial_T **partialp, int no_autoload) { dictitem_T *v; int cc; + char_u *s; if (partialp != NULL) *partialp = NULL; @@ -421,8 +345,9 @@ deref_func_name(char_u *name, int *lenp, partial_T **partialp, int no_autoload) *lenp = 0; return (char_u *)""; /* just in case */ } - *lenp = (int)STRLEN(v->di_tv.vval.v_string); - return v->di_tv.vval.v_string; + s |