diff options
author | Michael Ennen <mike.ennen@gmail.com> | 2016-12-16 14:51:49 -0700 |
---|---|---|
committer | Michael Ennen <mike.ennen@gmail.com> | 2017-02-14 17:38:18 -0700 |
commit | 53fad45115d3ee438dfb537d99ccf3b021ebc6b7 (patch) | |
tree | 9143f619985d8a02ce03fa5f56cee60c45d5f460 /src/nvim/eval.c | |
parent | 1f715ac1c1a1eee43360be911636020ed855e12c (diff) | |
download | rneovim-53fad45115d3ee438dfb537d99ccf3b021ebc6b7.tar.gz rneovim-53fad45115d3ee438dfb537d99ccf3b021ebc6b7.tar.bz2 rneovim-53fad45115d3ee438dfb537d99ccf3b021ebc6b7.zip |
vim-patch:7.4.2137
Problem: Using function() with a name will find another function when it is
redefined.
Solution: Add funcref(). Refer to lambda using a partial. Fix several
reference counting issues.
https://github.com/vim/vim/commit/437bafe4c8a83ed71ee006eda7f54b65a90f0d4c
Diffstat (limited to 'src/nvim/eval.c')
-rw-r--r-- | src/nvim/eval.c | 432 |
1 files changed, 255 insertions, 177 deletions
diff --git a/src/nvim/eval.c b/src/nvim/eval.c index 192afa0708..2e0ac6034d 100644 --- a/src/nvim/eval.c +++ b/src/nvim/eval.c @@ -218,7 +218,7 @@ static int echo_attr = 0; /* attributes used for ":echo" */ #define FC_DELETED 16 // :delfunction used while uf_refcount > 0 #define FC_REMOVED 32 // function redefined while uf_refcount > 0 -/* The names of packages that once were loaded are remembered. */ +// The names of packages that once were loaded are remembered. static garray_T ga_loaded = {0, 0, sizeof(char_u *), 4, NULL}; // List heads for garbage collection. Although there can be a reference loop @@ -232,37 +232,6 @@ static list_T *first_list = NULL; // list of all lists #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 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 - proftime_T prof_child; // time spent in a child - 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 -}; - /* * Info used by a ":for" loop. */ @@ -274,15 +243,6 @@ typedef struct { } forinfo_T; /* - * 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; - -/* * enum used by var_flavour() */ typedef enum { @@ -4960,6 +4920,15 @@ static int 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) { for (int i = 0; i < pt->pt_argc; i++) { @@ -4967,8 +4936,12 @@ static void partial_free(partial_T *pt) } xfree(pt->pt_argv); dict_unref(pt->pt_dict); - func_unref(pt->pt_name); - xfree(pt->pt_name); + if (pt->pt_name != NULL) { + func_unref(pt->pt_name); + xfree(pt->pt_name); + } else { + func_ptr_unref(pt->pt_func); + } xfree(pt); } @@ -5232,12 +5205,12 @@ static bool 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; } @@ -6272,7 +6245,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. if (pt != NULL) { - abort = abort || set_ref_in_func(pt->pt_name, copyID); + abort = abort || set_ref_in_func(pt->pt_name, pt->pt_func, copyID); if (pt->pt_dict != NULL) { typval_T dtv; @@ -6289,7 +6262,7 @@ bool set_ref_in_item(typval_T *tv, int copyID, ht_stack_T **ht_stack, break; } case VAR_FUNC: - abort = abort || set_ref_in_func(tv->vval.v_string, copyID); + abort = abort || set_ref_in_func(tv->vval.v_string, NULL, copyID); break; case VAR_UNKNOWN: case VAR_SPECIAL: @@ -6307,21 +6280,23 @@ bool set_ref_in_item(typval_T *tv, int copyID, ht_stack_T **ht_stack, /// "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) +int set_ref_in_func(char_u *name, ufunc_T *fp_in, int copyID) { - ufunc_T *fp; + ufunc_T *fp = fp_in; funccall_T *fc; int error; char_u fname_buf[FLEN_FIXED + 1]; char_u *tofree = NULL; char_u *fname; bool abort = false; - if (name == NULL) { + if (name == NULL && fp_in == NULL) { return false; } - fname = fname_trans_sid(name, fname_buf, &tofree, &error); - fp = find_func(fname); + if (fp_in == NULL) { + fname = fname_trans_sid(name, fname_buf, &tofree, &error); + fp = find_func(fname); + } if (fp != NULL) { for (fc = fp->uf_scoped; fc != NULL; fc = fc->func->uf_scoped) { if (fc->fc_copyID != copyID) { @@ -7026,7 +7001,7 @@ static int register_closure(ufunc_T *fp) { ga_grow(¤t_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); + func_ptr_ref(current_funccal->func); return OK; } @@ -7041,7 +7016,6 @@ static int 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; @@ -7092,6 +7066,8 @@ static int get_lambda_tv(char_u **arg, typval_T *rettv, int evaluate) if (evaluate) { int len, flags = 0; char_u *p; + char_u name[20]; + partial_T *pt; snprintf((char *)name, sizeof(name), "<lambda>%d", lambda_no++); @@ -7099,6 +7075,11 @@ static int get_lambda_tv(char_u **arg, typval_T *rettv, int evaluate) if (fp == NULL) { goto errret; } + pt = (partial_T *)xcalloc(1, (unsigned)(sizeof(partial_T))); + if (pt == NULL) { + xfree(fp); + goto errret; + } ga_init(&newlines, (int)sizeof(char_u *), 1); ga_grow(&newlines, 1); @@ -7138,8 +7119,10 @@ static int 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; return OK; @@ -7300,6 +7283,8 @@ static char_u *deref_func_name( { dictitem_T *v; int cc; + char_u *s; + if (partialp != NULL) { *partialp = NULL; } @@ -7313,8 +7298,9 @@ static char_u *deref_func_name( *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 = v->di_tv.vval.v_string; + *lenp = (int)STRLEN(s); + return s; } if (v != NULL && v->di_tv.v_type == VAR_PARTIAL) { @@ -7327,8 +7313,9 @@ static char_u *deref_func_name( if (partialp != NULL) { *partialp = pt; } - *lenp = (int)STRLEN(pt->pt_name); - return pt->pt_name; + s = partial_name(pt); + *lenp = (int)STRLEN(s); + return s; } return name; @@ -7421,6 +7408,7 @@ get_func_tv ( #define ERROR_NONE 5 #define ERROR_OTHER 6 #define ERROR_BOTH 7 +#define ERROR_DELETED 8 #define FLEN_FIXED 40 /// In a script change <SID>name() and s:name() to K_SNR 123_name(). @@ -7552,12 +7540,14 @@ call_func( error = ERROR_UNKNOWN; if (!builtin_function(rfname, -1)) { - /* - * User defined function. - */ - fp = find_func(rfname); + // User defined function. + if (partial != NULL && partial->pt_func != NULL) { + fp = partial->pt_func; + } else { + fp = find_func(rfname); + } - /* Trigger FuncUndefined event, may load the function. */ + // Trigger FuncUndefined event, may load the function. if (fp == NULL && apply_autocmds(EVENT_FUNCUNDEFINED, rfname, rfname, TRUE, NULL) && !aborting()) { @@ -7570,7 +7560,9 @@ call_func( fp = find_func(rfname); } - if (fp != NULL) { + if (fp != NULL && (fp->uf_flags & FC_DELETED)) { + error = ERROR_DELETED; + } else if (fp != NULL) { if (argv_func != NULL) { argcount = argv_func(argcount, argvars, fp->uf_args.ga_len); } @@ -7629,6 +7621,9 @@ call_func( case ERROR_UNKNOWN: emsg_funcname(N_("E117: Unknown function: %s"), name); break; + case ERROR_DELETED: + emsg_funcname(N_("E933: Function was deleted: %s"), name); + break; case ERROR_TOOMANY: emsg_funcname(e_toomanyarg, name); break; @@ -8465,7 +8460,7 @@ static void f_call(typval_T *argvars, typval_T *rettv, FunPtr fptr) func = argvars[0].vval.v_string; } 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]); } @@ -9725,7 +9720,7 @@ static int filter_map_one(typval_T *tv, typval_T *expr, int map, int *remp) } else if (expr->v_type == VAR_PARTIAL) { 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; @@ -10002,15 +9997,14 @@ static void f_foreground(typval_T *argvars, typval_T *rettv, FunPtr fptr) { } -/* - * "function()" function - */ -static void f_function(typval_T *argvars, typval_T *rettv, FunPtr fptr) +static void common_function(typval_T *argvars, typval_T *rettv, + bool is_funcref, FunPtr fptr) { char_u *s; char_u *name; bool use_string = false; partial_T *arg_pt = NULL; + char_u *trans_name = NULL; if (argvars[0].v_type == VAR_FUNC) { // function(MyFunc, [arg], dict) @@ -10019,17 +10013,28 @@ static void f_function(typval_T *argvars, typval_T *rettv, FunPtr fptr) && argvars[0].vval.v_partial != NULL) { // function(dict.MyFunc, [arg]) arg_pt = argvars[0].vval.v_partial; - s = arg_pt->pt_name; + s = partial_name(arg_pt); } else { // function('MyFunc', [arg], dict) s = get_tv_string(&argvars[0]); 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 != NULL) { + s = NULL; + } + } if (s == NULL || *s == NUL || (use_string && ascii_isdigit(*s))) { EMSG2(_(e_invarg2), s); - } 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))) { // Don't check an autoload name for existence here. EMSG2(_("E700: Unknown function: %s"), s); } else { @@ -10071,7 +10076,7 @@ static void f_function(typval_T *argvars, typval_T *rettv, FunPtr fptr) if (argvars[dict_idx].v_type != VAR_DICT) { EMSG(_("E922: expected a dict")); xfree(name); - return; + goto theend; } if (argvars[dict_idx].vval.v_dict == NULL) { dict_idx = 0; @@ -10082,7 +10087,7 @@ static void f_function(typval_T *argvars, typval_T *rettv, FunPtr fptr) EMSG(_("E923: Second argument of function() must be " "a list or a dict")); xfree(name); - return; + goto theend; } list = argvars[arg_idx].vval.v_list; if (list == NULL || list->lv_len == 0) { @@ -10090,7 +10095,7 @@ static void f_function(typval_T *argvars, typval_T *rettv, FunPtr fptr) } } } - if (dict_idx > 0 || arg_idx > 0 || arg_pt != NULL) { + if (dict_idx > 0 || arg_idx > 0 || arg_pt != NULL || is_funcref) { partial_T *const pt = xcalloc(1, sizeof(*pt)); // result is a VAR_PARTIAL @@ -10103,18 +10108,17 @@ static void f_function(typval_T *argvars, typval_T *rettv, FunPtr fptr) if (pt->pt_argv == NULL) { xfree(pt); xfree(name); - return; - } else { - int i = 0; - for (; i < arg_len; i++) { - copy_tv(&arg_pt->pt_argv[i], &pt->pt_argv[i]); - } - if (lv_len > 0) { - for (listitem_T *li = list->lv_first; - li != NULL; - li = li->li_next) { - copy_tv(&li->li_tv, &pt->pt_argv[i++]); - } + goto theend; + } + int i = 0; + for (; i < arg_len; i++) { + copy_tv(&arg_pt->pt_argv[i], &pt->pt_argv[i]); + } + if (lv_len > 0) { + for (listitem_T *li = list->lv_first; + li != NULL; + li = li->li_next) { + copy_tv(&li->li_tv, &pt->pt_argv[i++]); } } } @@ -10136,8 +10140,18 @@ static void f_function(typval_T *argvars, typval_T *rettv, FunPtr fptr) } 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); + xfree(name); + } else if (is_funcref) { + pt->pt_func = find_func(trans_name); + func_ptr_ref(pt->pt_func); + xfree(name); + } else { + pt->pt_name = name; + func_ref(name); + } rettv->v_type = VAR_PARTIAL; rettv->vval.v_partial = pt; @@ -10148,6 +10162,18 @@ static void f_function(typval_T *argvars, typval_T *rettv, FunPtr fptr) func_ref(name); } } +theend: + xfree(trans_name); +} + +static void f_funcref(typval_T *argvars, typval_T *rettv, FunPtr fptr) +{ + common_function(argvars, rettv, true, fptr); +} + +static void f_function(typval_T *argvars, typval_T *rettv, FunPtr fptr) +{ + common_function(argvars, rettv, false, fptr); } /// "garbagecollect()" function @@ -10201,11 +10227,18 @@ static void f_get(typval_T *argvars, typval_T *rettv, FunPtr fptr) 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) { - rettv->vval.v_string = vim_strsave(pt->pt_name); + n = partial_name(pt); + if (n == NULL) { + rettv->vval.v_string = NULL; + } else { + 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) { rettv->v_type = VAR_DICT; @@ -16243,8 +16276,9 @@ static int item_compare2(const void *s1, const void *s2, bool keep_zero) 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. copy_tv(&si1->item->li_tv, &argv[0]); @@ -17746,7 +17780,7 @@ static bool callback_call(Callback *callback, int argcount_in, case kCallbackPartial: partial = callback->data.partial; - name = partial->pt_name; + name = partial_name(partial); break; case kCallbackNone: @@ -19346,7 +19380,7 @@ handle_subscript( // Invoke the function. Recursive! 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; } @@ -19406,19 +19440,22 @@ static void set_selfdict(typval_T *rettv, dict_T *selfdict) && rettv->vval.v_partial->pt_dict != NULL) { return; } - char_u *fname = rettv->v_type == VAR_FUNC || rettv->v_type == VAR_STRING - ? rettv->vval.v_string - : rettv->vval.v_partial->pt_name; + char_u *fname; char_u *tofree = NULL; ufunc_T *fp; char_u fname_buf[FLEN_FIXED + 1]; int error; - // Translate "s:func" to the stored function name. - fname = fname_trans_sid(fname, fname_buf, &tofree, &error); - - fp = find_func(fname); - xfree(tofree); + if (rettv->v_type == VAR_PARTIAL && rettv->vval.v_partial->pt_func != NULL) { + fp = rettv->vval.v_partial->pt_func; + } else { + fname = rettv->v_type == VAR_FUNC ? rettv->vval.v_string + : rettv->vval.v_partial->pt_name; + // Translate "s:func" to the stored function name. + fname = fname_trans_sid(fname, fname_buf, &tofree, &error); + fp = find_func(fname); + xfree(tofree); + } // Turn "dict.Func" into a partial for "Func" with "dict". if (fp != NULL && (fp->uf_flags & FC_DICT)) { @@ -19439,8 +19476,13 @@ static void set_selfdict(typval_T *rettv, dict_T *selfdict) // Partial: copy the function name, use selfdict and copy // args. Can't take over name or args, the partial might // be referenced elsewhere. - pt->pt_name = vim_strsave(ret_pt->pt_name); - func_ref(pt->pt_name); + if (ret_pt->pt_name != NULL) { + pt->pt_name = vim_strsave(ret_pt->pt_name); + func_ref(pt->pt_name); + } else { + pt->pt_func = ret_pt->pt_func; + func_ptr_ref(pt->pt_func); + } if (ret_pt->pt_argc > 0) { size_t arg_size = sizeof(typval_T) * ret_pt->pt_argc; pt->pt_argv = (typval_T *)xmalloc(arg_size); @@ -21286,11 +21328,19 @@ void ex_function(exarg_T *eap) name); goto erret; } - /* redefine existing function */ - ga_clear_strings(&(fp->uf_args)); - ga_clear_strings(&(fp->uf_lines)); - xfree(name); - name = NULL; + if (fp->uf_refcount > 1) { + // This function is referenced somewhere, don't redefine it but + // create a new one. + (fp->uf_refcount)--; + fp = NULL; + overwrite = true; + } else { + // redefine existing function + ga_clear_strings(&(fp->uf_args)); + ga_clear_strings(&(fp->uf_lines)) + xfree(name); + name = NULL; + } } } else { char numbuf[20]; @@ -21366,12 +21416,15 @@ void ex_function(exarg_T *eap) /* insert the new function in the function list */ STRCPY(fp->uf_name, name); - if (hash_add(&func_hashtab, UF2HIKEY(fp)) == FAIL) { - xfree(fp); - goto erret; + if (overwrite) { + hi = hash_find(&func_hashtab, name); + hi->hi_key = UF2HIKEY(fp); + } else if (hash_add(&func_hashtab, UF2HIKEY(fp)) == FAIL) { + xfree(fp); + goto erret; } - } fp->uf_refcount = 1; + } fp->uf_args = newargs; fp->uf_lines = newlines; if ((flags & FC_CLOSURE) != 0) { @@ -21413,8 +21466,8 @@ ret_free: /// Advances "pp" to just after the function name (if no error). /// /// @return the function name in allocated memory, or NULL for failure. -static char_u * -trans_function_name ( +char_u * +trans_function_name( char_u **pp, int skip, /* only find the end, don't evaluate */ int flags, @@ -21479,7 +21532,7 @@ trans_function_name ( fdp->fd_di = lv.ll_di; } if (lv.ll_tv->v_type == VAR_FUNC && lv.ll_tv->vval.v_string != NULL) { - name = vim_strsave(lv.ll_tv->vval.v_string); + name = vim_strsave(partial_name(lv.ll_tv->vval.v_partial)); *pp = end; } else if (lv.ll_tv->v_type == VAR_PARTIAL && lv.ll_tv->vval.v_partial != NULL) { @@ -21676,11 +21729,9 @@ static void list_func_head(ufunc_T *fp, int indent) last_set_msg(fp->uf_script_ID); } -/* - * Find a function by name, return pointer to it in ufuncs. - * Return NULL for unknown function. - */ -static ufunc_T *find_func(char_u *name) +/// Find a function by name, return pointer to it in ufuncs. +/// @return NULL for unknown function. +ufunc_T *find_func(char_u *name) { hashitem_T *hi; @@ -22098,8 +22149,34 @@ void ex_delfunction(exarg_T *eap) /* Delete the dict item that refers to the function, it will * invoke func_unref() and possibly delete the function. */ dictitem_remove(fudi.fd_dict, fudi.fd_di); - } else - func_free(fp); + } else { + // Normal functions (not numbered functions and lambdas) have a + // refcount of 1 for the entry in the hashtable. When deleting + // them and the refcount is more than one, it should be kept. + // Numbered functions and lambdas snould be kept if the refcount is + // one or more. + if (fp->uf_refcount > (isdigit(fp->uf_name[0]) + || fp->uf_name[0] == '<') ? 0 : 1) { + // Function is still referenced somewhere. Don't free it but + // do remove it from the hashtable. + func_remove(fp); + fp->uf_flags |= FC_DELETED; + fp->uf_refcount--; + } else { + func_free(fp); + } + } + } +} + +/// Remove the function from the function hashtable. If the function was +/// deleted while it still has references this was already done. +static void func_remove(ufunc_T *fp) +{ + hashitem_T *hi = hash_find(&func_hashtab, UF2HIKEY(fp)); + + if (!HASHITEM_EMPTY(hi)) { + hash_remove(&func_hashtab, hi); } } @@ -22108,9 +22185,7 @@ void ex_delfunction(exarg_T *eap) */ static void func_free(ufunc_T *fp) { - hashitem_T *hi; - - /* clear this function */ + // clear this function ga_clear_strings(&(fp->uf_args)); ga_clear_strings(&(fp->uf_lines)); xfree(fp->uf_tml_count); @@ -22123,12 +22198,13 @@ static void func_free(ufunc_T *fp) func_remove(fp); } funccal_unref(fp->uf_scoped, fp); + func_remove(fp); xfree(fp); } /* * Unreference a Function: decrement the reference count and free it when it - * becomes zero. Only for numbered functions. + * becomes zero. */ void func_unref(char_u *name) { @@ -22154,6 +22230,10 @@ void func_unref(char_u *name) // fail silently, when lambda function isn't found fp = find_func(name); } + if (fp == NULL && isdigit(*name)) { + EMSG2(_(e_intern2), "func_unref()"); + } + if (fp != NULL && --fp->uf_refcount <= 0) { // Only delete it when it's not being used. Otherwise it's done // when "uf_calls" becomes zero. @@ -22163,10 +22243,12 @@ void func_unref(char_u *name) } } -static void user_func_unref(ufunc_T *fp) +/// Unreference a Function: decrement the reference count and free it when it +/// becomes zero. +void func_ptr_unref(ufunc_T *fp) { - if (--fp->uf_refcount <= 0) { - // Only delete it when it's not being used. Otherwise it's done + if (fp != NULL && --fp->uf_refcount <= 0) { + // Only delete it when it's not being used. Otherwise it's done // when "uf_calls" becomes zero. if (fp->uf_calls == 0) { func_free(fp); @@ -22174,36 +22256,35 @@ static void user_func_unref(ufunc_T *fp) } } -/* - * Count a reference to a Function. - */ +/// Count a reference to a Function. void func_ref(char_u *name) { ufunc_T *fp; if (name == NULL) { return; + } + fp = find_func(name); + if (fp != NULL) { + (fp->uf_refcount)++; } else if (isdigit(*name)) { - fp = find_func(name); - if (fp == NULL) { - EMSG2(_(e_intern2), "func_ref()"); - } else { - (fp->uf_refcount)++; - } - } else if (STRNCMP(name, "<lambda>", 8) == 0) { - // fail silently, when lambda function isn't found. - fp = find_func(name); - if (fp != NULL) { - (fp->uf_refcount)++; - } + // Only give an error for a numbered function. + // Fail silently, when named or lambda function isn't found. + EMSG2(_(e_intern2), "func_ref()"); } } -/* - * Call a user function. - */ -static void -call_user_func ( +/// Count a reference to a Function. +void func_ptr_ref(ufunc_T *fp) +{ + if (fp != NULL) { + (fp->uf_refcount)++; + } +} + +/// Call a user function. +static void +call_user_func( ufunc_T *fp, /* pointer to function */ int argcount, /* nr of args */ typval_T *argvars, /* arguments */ @@ -22264,7 +22345,7 @@ call_user_func ( fc->fc_refcount = 0; fc->fc_copyID = 0; ga_init(&fc->fc_funcs, sizeof(ufunc_T *), 1); - func_ref(fp->uf_name); + func_ptr_ref(fp); if (STRNCMP(fp->uf_name, "<lambda>", 8) == 0) { islambda = true; @@ -22579,9 +22660,7 @@ call_user_func ( copy_tv(&li->li_tv, &li->li_tv); } - if (--fp->uf_calls <= 0 && (isdigit(*fp->uf_name) - || STRNCMP(fp->uf_name, "<lambda>", 8) == 0) - && fp->uf_refcount <= 0) { + if (--fp->uf_calls <= 0 && fp->uf_refcount <= 0) { // Function was unreferenced while being used, free it now. func_free(fp); } @@ -22605,33 +22684,32 @@ static void funccal_unref(funccall_T *fc, ufunc_T *fp) } 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; + for (pfc = &previous_funccal; *pfc != NULL; pfc = &(*pfc)->caller) { + if (fc == *pfc) { + 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) { + *pfc = fc->caller; + free_funccal(fc, true); + freed = true; + } } } - if (!freed) { - func_unref(fc->func->uf_name); + } + if (!freed) { + func_ptr_unref(fc->func); - if (fp != NULL) { - for (i = 0; i < fc->fc_funcs.ga_len; i++) { - if (((ufunc_T **)(fc->fc_funcs.ga_data))[i] == fp) { + 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 +/// @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) { @@ -22661,10 +22739,10 @@ free_funccal ( if (fp->uf_scoped == fc) { fp->uf_scoped = NULL; } - func_unref(fc->func->uf_name); + func_ptr_unref(fc->func); } } - ga_clear(&fc->fc_funcs): + ga_clear(&fc->fc_funcs); // The a: variables typevals may not have been allocated, only free the // allocated variables. @@ -22680,7 +22758,7 @@ free_funccal ( } } - func_unref(fc->func->uf_name); + func_ptr_unref(fc->func); xfree(fc); } |