diff options
author | Matthieu Coudron <mattator@gmail.com> | 2020-05-25 14:01:36 +0200 |
---|---|---|
committer | GitHub <noreply@github.com> | 2020-05-25 14:01:36 +0200 |
commit | 8c588246a505e4fcd3d28e6d1f670e834c918529 (patch) | |
tree | b1ffbf9bc47870e4b86b37b14e9d638158e0014e /src | |
parent | b996205969d5c56e9110158e48cef559e9de0969 (diff) | |
parent | a532fb2cf425ef15dbbbee6dd00d1401c90aebae (diff) | |
download | rneovim-8c588246a505e4fcd3d28e6d1f670e834c918529.tar.gz rneovim-8c588246a505e4fcd3d28e6d1f670e834c918529.tar.bz2 rneovim-8c588246a505e4fcd3d28e6d1f670e834c918529.zip |
Merge pull request #10905 from erw7/vim-8.1.0475
vim-patch:8.1.{475,800,868,1007,1027,1031,1033,1037,1058,1435,1484,1485}
Diffstat (limited to 'src')
-rw-r--r-- | src/nvim/eval.c | 38 | ||||
-rw-r--r-- | src/nvim/eval/funcs.c | 8 | ||||
-rw-r--r-- | src/nvim/eval/typval_encode.c.h | 2 | ||||
-rw-r--r-- | src/nvim/eval/userfunc.c | 157 | ||||
-rw-r--r-- | src/nvim/eval/userfunc.h | 6 | ||||
-rw-r--r-- | src/nvim/ex_cmds2.c | 6 | ||||
-rw-r--r-- | src/nvim/fileio.c | 11 | ||||
-rw-r--r-- | src/nvim/testdir/runtest.vim | 16 |
8 files changed, 161 insertions, 83 deletions
diff --git a/src/nvim/eval.c b/src/nvim/eval.c index 7179e1569c..017d46e802 100644 --- a/src/nvim/eval.c +++ b/src/nvim/eval.c @@ -444,7 +444,7 @@ void eval_clear(void) // unreferenced lists and dicts (void)garbage_collect(false); - // functions + // functions not garbage collected free_all_functions(); } @@ -873,17 +873,19 @@ char_u *eval_to_string(char_u *arg, char_u **nextcmd, int convert) char_u *eval_to_string_safe(char_u *arg, char_u **nextcmd, int use_sandbox) { char_u *retval; - void *save_funccalp; + funccal_entry_T funccal_entry; - save_funccalp = save_funccal(); - if (use_sandbox) - ++sandbox; - ++textlock; - retval = eval_to_string(arg, nextcmd, FALSE); - if (use_sandbox) - --sandbox; - --textlock; - restore_funccal(save_funccalp); + save_funccal(&funccal_entry); + if (use_sandbox) { + sandbox++; + } + textlock++; + retval = eval_to_string(arg, nextcmd, false); + if (use_sandbox) { + sandbox--; + } + textlock--; + restore_funccal(); return retval; } @@ -5026,7 +5028,7 @@ bool garbage_collect(bool testing) // 3. Check if any funccal can be freed now. // This may call us back recursively. - did_free = did_free || free_unref_funccal(copyID, testing); + did_free = free_unref_funccal(copyID, testing) || did_free; } else if (p_verbose > 0) { verb_msg(_( "Not enough memory to set references, garbage collection aborted!")); @@ -9766,9 +9768,11 @@ const void *var_shada_iter(const void *const iter, const char **const name, void var_set_global(const char *const name, typval_T vartv) { - funccall_T *const saved_funccal = (funccall_T *)save_funccal(); + funccal_entry_T funccall_entry; + + save_funccal(&funccall_entry); set_var(name, strlen(name), &vartv, false); - restore_funccal(saved_funccal); + restore_funccal(); } int store_session_globals(FILE *fd) @@ -10324,8 +10328,10 @@ typval_T eval_call_provider(char *provider, char *method, list_T *arguments) .autocmd_fname = autocmd_fname, .autocmd_match = autocmd_match, .autocmd_bufnr = autocmd_bufnr, - .funccalp = save_funccal() + .funccalp = (void *)get_current_funccal() }; + funccal_entry_T funccal_entry; + save_funccal(&funccal_entry); provider_call_nesting++; typval_T argvars[3] = { @@ -10352,7 +10358,7 @@ typval_T eval_call_provider(char *provider, char *method, list_T *arguments) tv_list_unref(arguments); // Restore caller scope information - restore_funccal(provider_caller_scope.funccalp); + restore_funccal(); provider_caller_scope = saved_provider_caller_scope; provider_call_nesting--; assert(provider_call_nesting >= 0); diff --git a/src/nvim/eval/funcs.c b/src/nvim/eval/funcs.c index 79a52d9779..25beb4be50 100644 --- a/src/nvim/eval/funcs.c +++ b/src/nvim/eval/funcs.c @@ -7243,7 +7243,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 +7254,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 +7262,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 +7280,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)) { diff --git a/src/nvim/eval/typval_encode.c.h b/src/nvim/eval/typval_encode.c.h index af21a6fbe3..94986a64b5 100644 --- a/src/nvim/eval/typval_encode.c.h +++ b/src/nvim/eval/typval_encode.c.h @@ -607,7 +607,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..c054433255 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"); @@ -541,14 +541,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 +557,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 +666,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 +793,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 +908,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 +1135,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 +1162,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 +1176,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 +3163,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 +3356,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, diff --git a/src/nvim/ex_cmds2.c b/src/nvim/ex_cmds2.c index 9f4055af8d..7f4b01e306 100644 --- a/src/nvim/ex_cmds2.c +++ b/src/nvim/ex_cmds2.c @@ -3107,7 +3107,6 @@ int do_source(char_u *fname, int check_other, int is_vimrc) int retval = FAIL; static scid_T last_current_SID = 0; static int last_current_SID_seq = 0; - void *save_funccalp; int save_debug_break_level = debug_break_level; scriptitem_T *si = NULL; proftime_T wait_start; @@ -3228,7 +3227,8 @@ int do_source(char_u *fname, int check_other, int is_vimrc) // Don't use local function variables, if called from a function. // Also starts profiling timer for nested script. - save_funccalp = save_funccal(); + funccal_entry_T funccalp_entry; + save_funccal(&funccalp_entry); // Check if this script was sourced before to finds its SID. // If it's new, generate a new SID. @@ -3353,7 +3353,7 @@ int do_source(char_u *fname, int check_other, int is_vimrc) } current_sctx = save_current_sctx; - restore_funccal(save_funccalp); + restore_funccal(); if (l_do_profiling == PROF_YES) { prof_child_exit(&wait_start); // leaving a child now } diff --git a/src/nvim/fileio.c b/src/nvim/fileio.c index 2335aba6dd..f29304867a 100644 --- a/src/nvim/fileio.c +++ b/src/nvim/fileio.c @@ -6736,7 +6736,6 @@ static bool apply_autocmds_group(event_T event, char_u *fname, char_u *fname_io, static int nesting = 0; AutoPatCmd patcmd; AutoPat *ap; - void *save_funccalp; char_u *save_cmdarg; long save_cmdbang; static int filechangeshell_busy = FALSE; @@ -6926,8 +6925,9 @@ static bool apply_autocmds_group(event_T event, char_u *fname, char_u *fname_io, if (do_profiling == PROF_YES) prof_child_enter(&wait_time); /* doesn't count for the caller itself */ - /* Don't use local function variables, if called from a function */ - save_funccalp = save_funccal(); + // Don't use local function variables, if called from a function. + funccal_entry_T funccal_entry; + save_funccal(&funccal_entry); /* * When starting to execute autocommands, save the search patterns. @@ -7016,9 +7016,10 @@ static bool apply_autocmds_group(event_T event, char_u *fname, char_u *fname_io, autocmd_bufnr = save_autocmd_bufnr; autocmd_match = save_autocmd_match; current_sctx = save_current_sctx; - restore_funccal(save_funccalp); - if (do_profiling == PROF_YES) + restore_funccal(); + if (do_profiling == PROF_YES) { prof_child_exit(&wait_time); + } KeyTyped = save_KeyTyped; xfree(fname); xfree(sfname); diff --git a/src/nvim/testdir/runtest.vim b/src/nvim/testdir/runtest.vim index adf7463936..e249d499c4 100644 --- a/src/nvim/testdir/runtest.vim +++ b/src/nvim/testdir/runtest.vim @@ -44,6 +44,10 @@ if &lines < 24 || &columns < 80 qa! endif +if has('reltime') + let s:start_time = reltime() +endif + " Common with all tests on all systems. source setup.vim @@ -100,6 +104,9 @@ endfunc func RunTheTest(test) echo 'Executing ' . a:test + if has('reltime') + let func_start = reltime() + endif " Avoid stopping at the "hit enter" prompt set nomore @@ -124,7 +131,11 @@ func RunTheTest(test) endtry endif - call add(s:messages, 'Executing ' . a:test) + let message = 'Executed ' . a:test + if has('reltime') + let message ..= ' in ' .. reltimestr(reltime(func_start)) .. ' seconds' + endif + call add(s:messages, message) let s:done += 1 if a:test =~ 'Test_nocatch_' @@ -230,6 +241,9 @@ func FinishTesting() else let message = 'Executed ' . s:done . (s:done > 1 ? ' tests' : ' test') endif + if has('reltime') + let message ..= ' in ' .. reltimestr(reltime(s:start_time)) .. ' seconds' + endif echo message call add(s:messages, message) if s:fail > 0 |