diff options
Diffstat (limited to 'src/nvim/eval')
| -rw-r--r-- | src/nvim/eval/decode.c | 12 | ||||
| -rw-r--r-- | src/nvim/eval/encode.c | 7 | ||||
| -rw-r--r-- | src/nvim/eval/encode.h | 1 | ||||
| -rw-r--r-- | src/nvim/eval/executor.c | 4 | ||||
| -rw-r--r-- | src/nvim/eval/funcs.c | 63 | ||||
| -rw-r--r-- | src/nvim/eval/typval.c | 44 | ||||
| -rw-r--r-- | src/nvim/eval/typval.h | 10 | ||||
| -rw-r--r-- | src/nvim/eval/typval_encode.c.h | 17 | ||||
| -rw-r--r-- | src/nvim/eval/userfunc.c | 160 | ||||
| -rw-r--r-- | src/nvim/eval/userfunc.h | 6 |
10 files changed, 214 insertions, 110 deletions
diff --git a/src/nvim/eval/decode.c b/src/nvim/eval/decode.c index 42999ddd62..daba304f00 100644 --- a/src/nvim/eval/decode.c +++ b/src/nvim/eval/decode.c @@ -795,9 +795,9 @@ json_decode_string_cycle_start: } p += 3; POP(((typval_T) { - .v_type = VAR_SPECIAL, + .v_type = VAR_BOOL, .v_lock = VAR_UNLOCKED, - .vval = { .v_special = kSpecialVarTrue }, + .vval = { .v_bool = kBoolVarTrue }, }), false); break; } @@ -808,9 +808,9 @@ json_decode_string_cycle_start: } p += 4; POP(((typval_T) { - .v_type = VAR_SPECIAL, + .v_type = VAR_BOOL, .v_lock = VAR_UNLOCKED, - .vval = { .v_special = kSpecialVarFalse }, + .vval = { .v_bool = kBoolVarFalse }, }), false); break; } @@ -954,10 +954,10 @@ int msgpack_to_vim(const msgpack_object mobj, typval_T *const rettv) } case MSGPACK_OBJECT_BOOLEAN: { *rettv = (typval_T) { - .v_type = VAR_SPECIAL, + .v_type = VAR_BOOL, .v_lock = VAR_UNLOCKED, .vval = { - .v_special = mobj.via.boolean ? kSpecialVarTrue : kSpecialVarFalse + .v_bool = mobj.via.boolean ? kBoolVarTrue : kBoolVarFalse }, }; break; diff --git a/src/nvim/eval/encode.c b/src/nvim/eval/encode.c index 138f638eb2..137f099df6 100644 --- a/src/nvim/eval/encode.c +++ b/src/nvim/eval/encode.c @@ -34,10 +34,13 @@ #define utf_ptr2len(b) ((size_t)utf_ptr2len((char_u *)b)) #define utf_char2len(b) ((size_t)utf_char2len(b)) +const char *const encode_bool_var_names[] = { + [kBoolVarTrue] = "true", + [kBoolVarFalse] = "false", +}; + const char *const encode_special_var_names[] = { [kSpecialVarNull] = "null", - [kSpecialVarTrue] = "true", - [kSpecialVarFalse] = "false", }; #ifdef INCLUDE_GENERATED_DECLARATIONS diff --git a/src/nvim/eval/encode.h b/src/nvim/eval/encode.h index ccea245ab3..596bb49ae0 100644 --- a/src/nvim/eval/encode.h +++ b/src/nvim/eval/encode.h @@ -55,6 +55,7 @@ static inline ListReaderState encode_init_lrstate(const list_T *const list) } /// Array mapping values from SpecialVarValue enum to names +extern const char *const encode_bool_var_names[]; extern const char *const encode_special_var_names[]; /// First codepoint in high surrogates block diff --git a/src/nvim/eval/executor.c b/src/nvim/eval/executor.c index 8cd21f8d62..da05ecda43 100644 --- a/src/nvim/eval/executor.c +++ b/src/nvim/eval/executor.c @@ -28,11 +28,13 @@ int eexe_mod_op(typval_T *const tv1, const typval_T *const tv2, FUNC_ATTR_NONNULL_ALL FUNC_ATTR_NO_SANITIZE_UNDEFINED { // Can't do anything with a Funcref, a Dict or special value on the right. - if (tv2->v_type != VAR_FUNC && tv2->v_type != VAR_DICT) { + if (tv2->v_type != VAR_FUNC && tv2->v_type != VAR_DICT + && tv2->v_type != VAR_BOOL && tv2->v_type != VAR_SPECIAL) { switch (tv1->v_type) { case VAR_DICT: case VAR_FUNC: case VAR_PARTIAL: + case VAR_BOOL: case VAR_SPECIAL: { break; } diff --git a/src/nvim/eval/funcs.c b/src/nvim/eval/funcs.c index 79a52d9779..1071e75c06 100644 --- a/src/nvim/eval/funcs.c +++ b/src/nvim/eval/funcs.c @@ -175,8 +175,8 @@ static int non_zero_arg(typval_T *argvars) { return ((argvars[0].v_type == VAR_NUMBER && argvars[0].vval.v_number != 0) - || (argvars[0].v_type == VAR_SPECIAL - && argvars[0].vval.v_special == kSpecialVarTrue) + || (argvars[0].v_type == VAR_BOOL + && argvars[0].vval.v_bool == kBoolVarTrue) || (argvars[0].v_type == VAR_STRING && argvars[0].vval.v_string != NULL && *argvars[0].vval.v_string != NUL)); @@ -1758,21 +1758,23 @@ static void f_empty(typval_T *argvars, typval_T *rettv, FunPtr fptr) n = (tv_dict_len(argvars[0].vval.v_dict) == 0); break; } - case VAR_SPECIAL: { - // Using switch to get warning if SpecialVarValue receives more values. - switch (argvars[0].vval.v_special) { - case kSpecialVarTrue: { + case VAR_BOOL: { + switch (argvars[0].vval.v_bool) { + case kBoolVarTrue: { n = false; break; } - case kSpecialVarFalse: - case kSpecialVarNull: { + case kBoolVarFalse: { n = true; break; } } break; } + case VAR_SPECIAL: { + n = argvars[0].vval.v_special == kSpecialVarNull; + break; + } case VAR_UNKNOWN: { internal_error("f_empty(UNKNOWN)"); break; @@ -4860,6 +4862,7 @@ static void f_jobstart(typval_T *argvars, typval_T *rettv, FunPtr fptr) bool rpc = false; bool pty = false; bool clear_env = false; + bool overlapped = false; CallbackReader on_stdout = CALLBACK_READER_INIT, on_stderr = CALLBACK_READER_INIT; Callback on_exit = CALLBACK_NONE; @@ -4871,12 +4874,23 @@ static void f_jobstart(typval_T *argvars, typval_T *rettv, FunPtr fptr) rpc = tv_dict_get_number(job_opts, "rpc") != 0; pty = tv_dict_get_number(job_opts, "pty") != 0; clear_env = tv_dict_get_number(job_opts, "clear_env") != 0; + overlapped = tv_dict_get_number(job_opts, "overlapped") != 0; + if (pty && rpc) { EMSG2(_(e_invarg2), "job cannot have both 'pty' and 'rpc' options set"); shell_free_argv(argv); return; } +#ifdef WIN32 + if (pty && overlapped) { + EMSG2(_(e_invarg2), + "job cannot have both 'pty' and 'overlapped' options set"); + shell_free_argv(argv); + return; + } +#endif + char *new_cwd = tv_dict_get_string(job_opts, "cwd", false); if (new_cwd && strlen(new_cwd) > 0) { cwd = new_cwd; @@ -4943,7 +4957,7 @@ static void f_jobstart(typval_T *argvars, typval_T *rettv, FunPtr fptr) } Channel *chan = channel_job_start(argv, on_stdout, on_stderr, on_exit, pty, - rpc, detach, cwd, width, height, + rpc, overlapped, detach, cwd, width, height, term_name, env, &rettv->vval.v_number); if (chan) { channel_create_event(chan, NULL); @@ -5189,6 +5203,7 @@ static void f_len(typval_T *argvars, typval_T *rettv, FunPtr fptr) break; } case VAR_UNKNOWN: + case VAR_BOOL: case VAR_SPECIAL: case VAR_FLOAT: case VAR_PARTIAL: @@ -7243,7 +7258,7 @@ static void f_rpcrequest(typval_T *argvars, typval_T *rettv, FunPtr fptr) uint8_t *save_sourcing_name, *save_autocmd_fname, *save_autocmd_match; linenr_T save_sourcing_lnum; int save_autocmd_bufnr; - void *save_funccalp; + funccal_entry_T funccal_entry; if (l_provider_call_nesting) { // If this is called from a provider function, restore the scope @@ -7254,7 +7269,7 @@ static void f_rpcrequest(typval_T *argvars, typval_T *rettv, FunPtr fptr) save_autocmd_fname = autocmd_fname; save_autocmd_match = autocmd_match; save_autocmd_bufnr = autocmd_bufnr; - save_funccalp = save_funccal(); + save_funccal(&funccal_entry); current_sctx = provider_caller_scope.script_ctx; sourcing_name = provider_caller_scope.sourcing_name; @@ -7262,7 +7277,7 @@ static void f_rpcrequest(typval_T *argvars, typval_T *rettv, FunPtr fptr) autocmd_fname = provider_caller_scope.autocmd_fname; autocmd_match = provider_caller_scope.autocmd_match; autocmd_bufnr = provider_caller_scope.autocmd_bufnr; - restore_funccal(provider_caller_scope.funccalp); + set_current_funccal((funccall_T *)(provider_caller_scope.funccalp)); } @@ -7280,7 +7295,7 @@ static void f_rpcrequest(typval_T *argvars, typval_T *rettv, FunPtr fptr) autocmd_fname = save_autocmd_fname; autocmd_match = save_autocmd_match; autocmd_bufnr = save_autocmd_bufnr; - restore_funccal(save_funccalp); + restore_funccal(); } if (ERROR_SET(&err)) { @@ -7369,8 +7384,8 @@ static void f_rpcstart(typval_T *argvars, typval_T *rettv, FunPtr fptr) Channel *chan = channel_job_start(argv, CALLBACK_READER_INIT, CALLBACK_READER_INIT, CALLBACK_NONE, - false, true, false, NULL, 0, 0, NULL, NULL, - &rettv->vval.v_number); + false, true, false, false, NULL, 0, 0, + NULL, NULL, &rettv->vval.v_number); if (chan) { channel_create_event(chan, NULL); } @@ -10458,7 +10473,7 @@ static void f_termopen(typval_T *argvars, typval_T *rettv, FunPtr fptr) uint16_t term_width = MAX(0, curwin->w_width_inner - win_col_off(curwin)); Channel *chan = channel_job_start(argv, on_stdout, on_stderr, on_exit, - true, false, false, cwd, + true, false, false, false, cwd, term_width, curwin->w_height_inner, xstrdup("xterm-256color"), NULL, &rettv->vval.v_number); @@ -10808,20 +10823,8 @@ static void f_type(typval_T *argvars, typval_T *rettv, FunPtr fptr) case VAR_LIST: n = VAR_TYPE_LIST; break; case VAR_DICT: n = VAR_TYPE_DICT; break; case VAR_FLOAT: n = VAR_TYPE_FLOAT; break; - case VAR_SPECIAL: { - switch (argvars[0].vval.v_special) { - case kSpecialVarTrue: - case kSpecialVarFalse: { - n = VAR_TYPE_BOOL; - break; - } - case kSpecialVarNull: { - n = 7; - break; - } - } - break; - } + case VAR_BOOL: n = VAR_TYPE_BOOL; break; + case VAR_SPECIAL:n = VAR_TYPE_SPECIAL; break; case VAR_UNKNOWN: { internal_error("f_type(UNKNOWN)"); break; diff --git a/src/nvim/eval/typval.c b/src/nvim/eval/typval.c index 35130f6f40..0daaf6c878 100644 --- a/src/nvim/eval/typval.c +++ b/src/nvim/eval/typval.c @@ -1693,21 +1693,21 @@ int tv_dict_add_float(dict_T *const d, const char *const key, return OK; } -/// Add a special entry to dictionary +/// Add a boolean entry to dictionary /// /// @param[out] d Dictionary to add entry to. /// @param[in] key Key to add. /// @param[in] key_len Key length. -/// @param[in] val SpecialVarValue to add. +/// @param[in] val BoolVarValue to add. /// /// @return OK in case of success, FAIL when key already exists. -int tv_dict_add_special(dict_T *const d, const char *const key, - const size_t key_len, SpecialVarValue val) +int tv_dict_add_bool(dict_T *const d, const char *const key, + const size_t key_len, BoolVarValue val) { dictitem_T *const item = tv_dict_item_alloc_len(key, key_len); - item->di_tv.v_type = VAR_SPECIAL; - item->di_tv.vval.v_special = val; + item->di_tv.v_type = VAR_BOOL; + item->di_tv.vval.v_bool = val; if (tv_dict_add(d, item) == FAIL) { tv_dict_item_free(item); return FAIL; @@ -2013,12 +2013,15 @@ void tv_dict_alloc_ret(typval_T *const ret_tv) #define TYPVAL_ENCODE_CONV_NIL(tv) \ do { \ - tv->vval.v_special = kSpecialVarFalse; \ + tv->vval.v_special = kSpecialVarNull; \ tv->v_lock = VAR_UNLOCKED; \ } while (0) #define TYPVAL_ENCODE_CONV_BOOL(tv, num) \ - TYPVAL_ENCODE_CONV_NIL(tv) + do { \ + tv->vval.v_bool = kBoolVarFalse; \ + tv->v_lock = VAR_UNLOCKED; \ + } while (0) #define TYPVAL_ENCODE_CONV_NUMBER(tv, num) \ do { \ @@ -2293,6 +2296,7 @@ void tv_free(typval_T *tv) tv_dict_unref(tv->vval.v_dict); break; } + case VAR_BOOL: case VAR_SPECIAL: case VAR_NUMBER: case VAR_FLOAT: @@ -2324,6 +2328,7 @@ void tv_copy(const typval_T *const from, typval_T *const to) switch (from->v_type) { case VAR_NUMBER: case VAR_FLOAT: + case VAR_BOOL: case VAR_SPECIAL: { break; } @@ -2425,6 +2430,7 @@ void tv_item_lock(typval_T *const tv, const int deep, const bool lock) case VAR_STRING: case VAR_FUNC: case VAR_PARTIAL: + case VAR_BOOL: case VAR_SPECIAL: { break; } @@ -2588,6 +2594,9 @@ bool tv_equal(typval_T *const tv1, typval_T *const tv2, const bool ic, const char *s2 = tv_get_string_buf(tv2, buf2); return mb_strcmp_ic((bool)ic, s1, s2) == 0; } + case VAR_BOOL: { + return tv1->vval.v_bool == tv2->vval.v_bool; + } case VAR_SPECIAL: { return tv1->vval.v_special == tv2->vval.v_special; } @@ -2638,6 +2647,10 @@ bool tv_check_str_or_nr(const typval_T *const tv) EMSG(_("E728: Expected a Number or a String, Dictionary found")); return false; } + case VAR_BOOL: { + EMSG(_("E5299: Expected a Number or a String, Boolean found")); + return false; + } case VAR_SPECIAL: { EMSG(_("E5300: Expected a Number or a String")); return false; @@ -2677,6 +2690,7 @@ bool tv_check_num(const typval_T *const tv) { switch (tv->v_type) { case VAR_NUMBER: + case VAR_BOOL: case VAR_SPECIAL: case VAR_STRING: { return true; @@ -2721,6 +2735,7 @@ bool tv_check_str(const typval_T *const tv) { switch (tv->v_type) { case VAR_NUMBER: + case VAR_BOOL: case VAR_SPECIAL: case VAR_STRING: { return true; @@ -2791,8 +2806,11 @@ varnumber_T tv_get_number_chk(const typval_T *const tv, bool *const ret_error) } return n; } + case VAR_BOOL: { + return tv->vval.v_bool == kBoolVarTrue ? 1 : 0; + } case VAR_SPECIAL: { - return tv->vval.v_special == kSpecialVarTrue ? 1 : 0; + return 0; } case VAR_UNKNOWN: { emsgf(_(e_intern2), "tv_get_number(UNKNOWN)"); @@ -2860,6 +2878,10 @@ float_T tv_get_float(const typval_T *const tv) EMSG(_("E894: Using a Dictionary as a Float")); break; } + case VAR_BOOL: { + EMSG(_("E362: Using a boolean value as a Float")); + break; + } case VAR_SPECIAL: { EMSG(_("E907: Using a special value as a Float")); break; @@ -2897,6 +2919,10 @@ const char *tv_get_string_buf_chk(const typval_T *const tv, char *const buf) } return ""; } + case VAR_BOOL: { + STRCPY(buf, encode_bool_var_names[tv->vval.v_bool]); + return buf; + } case VAR_SPECIAL: { STRCPY(buf, encode_special_var_names[tv->vval.v_special]); return buf; diff --git a/src/nvim/eval/typval.h b/src/nvim/eval/typval.h index 4390db1b71..343dd205ff 100644 --- a/src/nvim/eval/typval.h +++ b/src/nvim/eval/typval.h @@ -91,10 +91,14 @@ typedef struct dict_watcher { bool busy; // prevent recursion if the dict is changed in the callback } DictWatcher; +/// Bool variable values +typedef enum { + kBoolVarFalse, ///< v:false + kBoolVarTrue, ///< v:true +} BoolVarValue; + /// Special variable values typedef enum { - kSpecialVarFalse, ///< v:false - kSpecialVarTrue, ///< v:true kSpecialVarNull, ///< v:null } SpecialVarValue; @@ -114,6 +118,7 @@ typedef enum { VAR_LIST, ///< List, .v_list is used. VAR_DICT, ///< Dictionary, .v_dict is used. VAR_FLOAT, ///< Floating-point value, .v_float is used. + VAR_BOOL, ///< true, false VAR_SPECIAL, ///< Special value (true, false, null), .v_special ///< is used. VAR_PARTIAL, ///< Partial, .v_partial is used. @@ -125,6 +130,7 @@ typedef struct { VarLockStatus v_lock; ///< Variable lock status. union typval_vval_union { varnumber_T v_number; ///< Number, for VAR_NUMBER. + BoolVarValue v_bool; ///< Bool value, for VAR_BOOL SpecialVarValue v_special; ///< Special value, for VAR_SPECIAL. float_T v_float; ///< Floating-point number, for VAR_FLOAT. char_u *v_string; ///< String, for VAR_STRING and VAR_FUNC, can be NULL. diff --git a/src/nvim/eval/typval_encode.c.h b/src/nvim/eval/typval_encode.c.h index af21a6fbe3..0aa64b1d5f 100644 --- a/src/nvim/eval/typval_encode.c.h +++ b/src/nvim/eval/typval_encode.c.h @@ -379,17 +379,22 @@ static int _TYPVAL_ENCODE_CONVERT_ONE_VALUE( TYPVAL_ENCODE_CONV_REAL_LIST_AFTER_START(tv, _mp_last(*mpstack)); break; } + case VAR_BOOL: { + switch (tv->vval.v_bool) { + case kBoolVarTrue: + case kBoolVarFalse: { + TYPVAL_ENCODE_CONV_BOOL(tv, tv->vval.v_bool == kBoolVarTrue); + break; + } + } + break; + } case VAR_SPECIAL: { switch (tv->vval.v_special) { case kSpecialVarNull: { TYPVAL_ENCODE_CONV_NIL(tv); // -V1037 break; } - case kSpecialVarTrue: - case kSpecialVarFalse: { - TYPVAL_ENCODE_CONV_BOOL(tv, tv->vval.v_special == kSpecialVarTrue); - break; - } } break; } @@ -607,7 +612,7 @@ _convert_one_value_regular_dict: {} kMPConvDict); TYPVAL_ENCODE_CONV_DICT_START(tv, tv->vval.v_dict, tv->vval.v_dict->dv_hashtab.ht_used); - assert(saved_copyID != copyID && saved_copyID != copyID - 1); + assert(saved_copyID != copyID); _mp_push(*mpstack, ((MPConvStackVal) { .tv = tv, .type = kMPConvDict, diff --git a/src/nvim/eval/userfunc.c b/src/nvim/eval/userfunc.c index ae8557a8bc..4d658498c1 100644 --- a/src/nvim/eval/userfunc.c +++ b/src/nvim/eval/userfunc.c @@ -43,11 +43,11 @@ hashtab_T func_hashtab; static garray_T funcargs = GA_EMPTY_INIT_VALUE; // pointer to funccal for currently active function -funccall_T *current_funccal = NULL; +static funccall_T *current_funccal = NULL; // Pointer to list of previously used funccal, still around because some // item in it is still being used. -funccall_T *previous_funccal = NULL; +static funccall_T *previous_funccal = NULL; static char *e_funcexts = N_( "E122: Function %s already exists, add ! to replace it"); @@ -170,6 +170,7 @@ int get_lambda_tv(char_u **arg, typval_T *rettv, bool evaluate) garray_T newargs = GA_EMPTY_INIT_VALUE; garray_T *pnewargs; ufunc_T *fp = NULL; + partial_T *pt = NULL; int varargs; int ret; char_u *start = skipwhite(*arg + 1); @@ -219,7 +220,6 @@ int get_lambda_tv(char_u **arg, typval_T *rettv, bool evaluate) int len, flags = 0; char_u *p; char_u name[20]; - partial_T *pt; garray_T newlines; lambda_no++; @@ -274,6 +274,7 @@ int get_lambda_tv(char_u **arg, typval_T *rettv, bool evaluate) errret: ga_clear_strings(&newargs); xfree(fp); + xfree(pt); eval_lavars_used = old_eval_lavars; return FAIL; } @@ -541,14 +542,8 @@ static void add_nr_var(dict_T *dp, dictitem_T *v, char *name, varnumber_T nr) v->di_tv.vval.v_number = nr; } -/* - * Free "fc" and what it contains. - */ -static void -free_funccal( - funccall_T *fc, - int free_val // a: vars were allocated -) +// Free "fc" +static void free_funccal(funccall_T *fc) { for (int i = 0; i < fc->fc_funcs.ga_len; i++) { ufunc_T *fp = ((ufunc_T **)(fc->fc_funcs.ga_data))[i]; @@ -563,56 +558,89 @@ free_funccal( } ga_clear(&fc->fc_funcs); - // The a: variables typevals may not have been allocated, only free the - // allocated variables. - vars_clear_ext(&fc->l_avars.dv_hashtab, free_val); + func_ptr_unref(fc->func); + xfree(fc); +} +// Free "fc" and what it contains. +// Can be called only when "fc" is kept beyond the period of it called, +// i.e. after cleanup_function_call(fc). +static void free_funccal_contents(funccall_T *fc) +{ // Free all l: variables. vars_clear(&fc->l_vars.dv_hashtab); - // Free the a:000 variables if they were allocated. - if (free_val) { - TV_LIST_ITER(&fc->l_varlist, li, { - tv_clear(TV_LIST_ITEM_TV(li)); - }); - } + // Free all a: variables. + vars_clear(&fc->l_avars.dv_hashtab); - func_ptr_unref(fc->func); - xfree(fc); + // Free the a:000 variables. + TV_LIST_ITER(&fc->l_varlist, li, { + tv_clear(TV_LIST_ITEM_TV(li)); + }); + + free_funccal(fc); } /// Handle the last part of returning from a function: free the local hashtable. /// Unless it is still in use by a closure. static void cleanup_function_call(funccall_T *fc) { + bool may_free_fc = fc->fc_refcount <= 0; + bool free_fc = true; + current_funccal = fc->caller; - // If the a:000 list and the l: and a: dicts are not referenced and there - // is no closure using it, we can free the funccall_T and what's in it. - if (!fc_referenced(fc)) { - free_funccal(fc, false); + // Free all l: variables if not referred. + if (may_free_fc && fc->l_vars.dv_refcount == DO_NOT_FREE_CNT) { + vars_clear(&fc->l_vars.dv_hashtab); } else { - static int made_copy = 0; + free_fc = false; + } - // "fc" is still in use. This can happen when returning "a:000", - // assigning "l:" to a global variable or defining a closure. - // Link "fc" in the list for garbage collection later. - fc->caller = previous_funccal; - previous_funccal = fc; + // If the a:000 list and the l: and a: dicts are not referenced and + // there is no closure using it, we can free the funccall_T and what's + // in it. + if (may_free_fc && fc->l_avars.dv_refcount == DO_NOT_FREE_CNT) { + vars_clear_ext(&fc->l_avars.dv_hashtab, false); + } else { + free_fc = false; // Make a copy of the a: variables, since we didn't do that above. TV_DICT_ITER(&fc->l_avars, di, { tv_copy(&di->di_tv, &di->di_tv); }); + } + + if (may_free_fc && fc->l_varlist.lv_refcount // NOLINT(runtime/deprecated) + == DO_NOT_FREE_CNT) { + fc->l_varlist.lv_first = NULL; // NOLINT(runtime/deprecated) + + } else { + free_fc = false; // Make a copy of the a:000 items, since we didn't do that above. TV_LIST_ITER(&fc->l_varlist, li, { tv_copy(TV_LIST_ITEM_TV(li), TV_LIST_ITEM_TV(li)); }); + } - if (++made_copy == 10000) { - // We have made a lot of copies. This can happen when - // repetitively calling a function that creates a reference to + if (free_fc) { + free_funccal(fc); + } else { + static int made_copy = 0; + + // "fc" is still in use. This can happen when returning "a:000", + // assigning "l:" to a global variable or defining a closure. + // Link "fc" in the list for garbage collection later. + fc->caller = previous_funccal; + previous_funccal = fc; + + if (want_garbage_collect) { + // If garbage collector is ready, clear count. + made_copy = 0; + } else if (++made_copy >= (int)((4096 * 1024) / sizeof(*fc))) { + // We have made a lot of copies, worth 4 Mbyte. This can happen + // when repetitively calling a function that creates a reference to // itself somehow. Call the garbage collector soon to avoid using // too much memory. made_copy = 0; @@ -639,7 +667,7 @@ static void funccal_unref(funccall_T *fc, ufunc_T *fp, bool force) for (pfc = &previous_funccal; *pfc != NULL; pfc = &(*pfc)->caller) { if (fc == *pfc) { *pfc = fc->caller; - free_funccal(fc, true); + free_funccal_contents(fc); return; } } @@ -766,7 +794,7 @@ void call_user_func(ufunc_T *fp, int argcount, typval_T *argvars, // check for CTRL-C hit line_breakcheck(); // prepare the funccall_T structure - fc = xmalloc(sizeof(funccall_T)); + fc = xcalloc(1, sizeof(funccall_T)); fc->caller = current_funccal; current_funccal = fc; fc->func = fp; @@ -881,9 +909,11 @@ void call_user_func(ufunc_T *fp, int argcount, typval_T *argvars, } if (ai >= 0 && ai < MAX_FUNC_ARGS) { - tv_list_append(&fc->l_varlist, &fc->l_listitems[ai]); - *TV_LIST_ITEM_TV(&fc->l_listitems[ai]) = argvars[i]; - TV_LIST_ITEM_TV(&fc->l_listitems[ai])->v_lock = VAR_FIXED; + listitem_T *li = &fc->l_listitems[ai]; + + *TV_LIST_ITEM_TV(li) = argvars[i]; + TV_LIST_ITEM_TV(li)->v_lock = VAR_FIXED; + tv_list_append(&fc->l_varlist, li); } } @@ -1106,21 +1136,26 @@ static bool func_name_refcount(char_u *name) return isdigit(*name) || *name == '<'; } -/* - * Save the current function call pointer, and set it to NULL. - * Used when executing autocommands and for ":source". - */ -void *save_funccal(void) -{ - funccall_T *fc = current_funccal; +static funccal_entry_T *funccal_stack = NULL; +// Save the current function call pointer, and set it to NULL. +// Used when executing autocommands and for ":source". +void save_funccal(funccal_entry_T *entry) +{ + entry->top_funccal = current_funccal; + entry->next = funccal_stack; + funccal_stack = entry; current_funccal = NULL; - return (void *)fc; } -void restore_funccal(void *vfc) +void restore_funccal(void) { - current_funccal = (funccall_T *)vfc; + if (funccal_stack == NULL) { + IEMSG("INTERNAL: restore_funccal()"); + } else { + current_funccal = funccal_stack->top_funccal; + funccal_stack = funccal_stack->next; + } } funccall_T *get_current_funccal(void) @@ -1128,6 +1163,11 @@ funccall_T *get_current_funccal(void) return current_funccal; } +void set_current_funccal(funccall_T *fc) +{ + current_funccal = fc; +} + #if defined(EXITFREE) void free_all_functions(void) { @@ -1137,10 +1177,13 @@ void free_all_functions(void) uint64_t todo = 1; uint64_t used; - // Clean up the call stack. + // Clean up the current_funccal chain and the funccal stack. while (current_funccal != NULL) { tv_clear(current_funccal->rettv); cleanup_function_call(current_funccal); + if (current_funccal == NULL && funccal_stack != NULL) { + restore_funccal(); + } } // First clear what the functions contain. Since this may lower the @@ -3121,7 +3164,7 @@ bool free_unref_funccal(int copyID, int testing) if (can_free_funccal(*pfc, copyID)) { funccall_T *fc = *pfc; *pfc = fc->caller; - free_funccal(fc, true); + free_funccal_contents(fc); did_free = true; did_free_funccal = true; } else { @@ -3314,9 +3357,18 @@ bool set_ref_in_call_stack(int copyID) bool abort = false; for (funccall_T *fc = current_funccal; fc != NULL; fc = fc->caller) { - 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); + abort = abort || set_ref_in_funccal(fc, copyID); } + + // Also go through the funccal_stack. + for (funccal_entry_T *entry = funccal_stack; entry != NULL; + entry = entry->next) { + for (funccall_T *fc = entry->top_funccal; !abort && fc != NULL; + fc = fc->caller) { + abort = abort || set_ref_in_funccal(fc, copyID); + } + } + return abort; } diff --git a/src/nvim/eval/userfunc.h b/src/nvim/eval/userfunc.h index ad8e071548..e8ad0bf1da 100644 --- a/src/nvim/eval/userfunc.h +++ b/src/nvim/eval/userfunc.h @@ -11,6 +11,12 @@ typedef struct { dictitem_T *fd_di; ///< Dictionary item used. } funcdict_T; +typedef struct funccal_entry funccal_entry_T; +struct funccal_entry { + void *top_funccal; + funccal_entry_T *next; +}; + /// errors for when calling a function typedef enum { ERROR_UNKNOWN = 0, |