# HG changeset patch # User Bram Moolenaar # Date 1539546304 -7200 # Node ID 162d79d273e60f256f57be65c4c6230927417294 # Parent f60f8f21f76f6f0e3057ed186635a7c2ba11516d patch 8.1.0475: memory not freed on exit when quit in autocmd commit https://github.com/vim/vim/commit/27e80c885bcb5c5cf6a6462d71d6c81b06ba2451 Author: Bram Moolenaar Date: Sun Oct 14 21:41:01 2018 +0200 patch 8.1.0475: memory not freed on exit when quit in autocmd Problem: Memory not freed on exit when quit in autocmd. Solution: Remember funccal stack when executing autocmd. diff --git a/src/eval.c b/src/eval.c --- a/src/eval.c +++ b/src/eval.c @@ -859,9 +859,9 @@ eval_to_string_safe( int use_sandbox) { char_u *retval; - void *save_funccalp; - - save_funccalp = save_funccal(); + funccal_entry_T funccal_entry; + + save_funccal(&funccal_entry); if (use_sandbox) ++sandbox; ++textlock; @@ -869,7 +869,7 @@ eval_to_string_safe( if (use_sandbox) --sandbox; --textlock; - restore_funccal(save_funccalp); + restore_funccal(); return retval; } @@ -8532,7 +8532,7 @@ read_viminfo_varlist(vir_T *virp, int wr char_u *tab; int type = VAR_NUMBER; typval_T tv; - void *save_funccal; + funccal_entry_T funccal_entry; if (!writing && (find_viminfo_parameter('!') != NULL)) { @@ -8581,9 +8581,9 @@ read_viminfo_varlist(vir_T *virp, int wr } /* when in a function use global variables */ - save_funccal = clear_current_funccal(); + save_funccal(&funccal_entry); set_var(virp->vir_line + 1, &tv, FALSE); - restore_current_funccal(save_funccal); + restore_funccal(); if (tv.v_type == VAR_STRING) vim_free(tv.vval.v_string); diff --git a/src/ex_cmds2.c b/src/ex_cmds2.c --- a/src/ex_cmds2.c +++ b/src/ex_cmds2.c @@ -4344,7 +4344,7 @@ do_source( #ifdef FEAT_EVAL sctx_T save_current_sctx; static scid_T last_current_SID = 0; - void *save_funccalp; + funccal_entry_T funccalp_entry; int save_debug_break_level = debug_break_level; scriptitem_T *si = NULL; # ifdef UNIX @@ -4506,7 +4506,7 @@ do_source( /* Don't use local function variables, if called from a function. * Also starts profiling timer for nested script. */ - save_funccalp = save_funccal(); + save_funccal(&funccalp_entry); /* * Check if this script was sourced before to finds its SID. @@ -4665,7 +4665,7 @@ do_source( #ifdef FEAT_EVAL almosttheend: current_sctx = save_current_sctx; - restore_funccal(save_funccalp); + restore_funccal(); # ifdef FEAT_PROFILE if (do_profiling == PROF_YES) prof_child_exit(&wait_start); /* leaving a child now */ diff --git a/src/fileio.c b/src/fileio.c --- a/src/fileio.c +++ b/src/fileio.c @@ -9400,7 +9400,7 @@ apply_autocmds_group( AutoPat *ap; #ifdef FEAT_EVAL sctx_T save_current_sctx; - void *save_funccalp; + funccal_entry_T funccal_entry; char_u *save_cmdarg; long save_cmdbang; #endif @@ -9615,8 +9615,8 @@ apply_autocmds_group( prof_child_enter(&wait_time); /* doesn't count for the caller itself */ # endif - /* 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. + save_funccal(&funccal_entry); #endif /* @@ -9713,7 +9713,7 @@ apply_autocmds_group( autocmd_match = save_autocmd_match; #ifdef FEAT_EVAL current_sctx = save_current_sctx; - restore_funccal(save_funccalp); + restore_funccal(); # ifdef FEAT_PROFILE if (do_profiling == PROF_YES) prof_child_exit(&wait_time); diff --git a/src/main.c b/src/main.c --- a/src/main.c +++ b/src/main.c @@ -1717,7 +1717,7 @@ get_number_arg( } /* - * Check for: [r][e][g][vi|vim|view][diff][ex[im]] + * Check for: [r][e][g][vi|vim|view][diff][ex[im]] (sort of) * If the executable name starts with "r" we disable shell commands. * If the next character is "e" we run in Easy mode. * If the next character is "g" we run the GUI version. @@ -1788,7 +1788,7 @@ parse_command_name(mparm_T *parmp) else if (STRNICMP(initstr, "vim", 3) == 0) initstr += 3; - /* Catch "[r][g]vimdiff" and "[r][g]viewdiff". */ + // Catch "[r][g]vimdiff" and "[r][g]viewdiff". if (STRICMP(initstr, "diff") == 0) { #ifdef FEAT_DIFF @@ -1800,13 +1800,15 @@ parse_command_name(mparm_T *parmp) #endif } + // Checking for "ex" here may catch some weir names, such as "vimex" or + // "viewex", we assume the user knows that. if (STRNICMP(initstr, "ex", 2) == 0) { if (STRNICMP(initstr + 2, "im", 2) == 0) exmode_active = EXMODE_VIM; else exmode_active = EXMODE_NORMAL; - change_compatible(TRUE); /* set 'compatible' */ + change_compatible(TRUE); // set 'compatible' } } @@ -4188,12 +4190,16 @@ eval_client_expr_to_string(char_u *expr) char_u *res; int save_dbl = debug_break_level; int save_ro = redir_off; - void *fc = NULL; + funccal_entry_T funccal_entry; + int did_save_funccal = FALSE; /* Evaluate the expression at the toplevel, don't use variables local to * the calling function. Except when in debug mode. */ if (!debug_mode) - fc = clear_current_funccal(); + { + save_funccal(&funccal_entry); + did_save_funccal = TRUE; + } /* Disable debugging, otherwise Vim hangs, waiting for "cont" to be * typed. */ @@ -4210,8 +4216,8 @@ eval_client_expr_to_string(char_u *expr) --emsg_silent; if (emsg_silent < 0) emsg_silent = 0; - if (fc != NULL) - restore_current_funccal(fc); + if (did_save_funccal) + restore_funccal(); /* A client can tell us to redraw, but not to display the cursor, so do * that here. */ diff --git a/src/proto/userfunc.pro b/src/proto/userfunc.pro --- a/src/proto/userfunc.pro +++ b/src/proto/userfunc.pro @@ -39,15 +39,13 @@ linenr_T *func_breakpoint(void *cookie); int *func_dbg_tick(void *cookie); int func_level(void *cookie); int current_func_returned(void); -void *save_funccal(void); -void restore_funccal(void *vfc); +void save_funccal(funccal_entry_T *entry); +void restore_funccal(void); int free_unref_funccal(int copyID, int testing); hashtab_T *get_funccal_local_ht(void); dictitem_T *get_funccal_local_var(void); hashtab_T *get_funccal_args_ht(void); dictitem_T *get_funccal_args_var(void); -void *clear_current_funccal(void); -void restore_current_funccal(void *f); void list_func_vars(int *first); dict_T *get_current_funccal_dict(hashtab_T *ht); hashitem_T *find_hi_in_scoped_ht(char_u *name, hashtab_T **pht); diff --git a/src/structs.h b/src/structs.h --- a/src/structs.h +++ b/src/structs.h @@ -1354,7 +1354,7 @@ typedef struct int uf_cleared; /* func_clear() was already called */ garray_T uf_args; /* arguments */ garray_T uf_lines; /* function lines */ -#ifdef FEAT_PROFILE +# ifdef FEAT_PROFILE int uf_profiling; /* TRUE when func is being profiled */ int uf_prof_initialized; /* profiling the function as a whole */ @@ -1371,7 +1371,7 @@ typedef struct 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 +# endif sctx_T uf_script_ctx; /* SCTX where function was defined, used for s: variables */ int uf_refcount; /* reference count, see func_name_refcount() */ @@ -1429,6 +1429,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; +}; + #else /* dummy typedefs for function prototypes */ typedef struct diff --git a/src/userfunc.c b/src/userfunc.c --- a/src/userfunc.c +++ b/src/userfunc.c @@ -1175,6 +1175,33 @@ func_name_refcount(char_u *name) return isdigit(*name) || *name == '<'; } +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; +} + + void +restore_funccal(void) +{ + if (funccal_stack == NULL) + IEMSG("INTERNAL: restore_funccal()"); + else + { + current_funccal = funccal_stack->top_funccal; + funccal_stack = funccal_stack->next; + } +} + #if defined(EXITFREE) || defined(PROTO) void free_all_functions(void) @@ -1185,11 +1212,13 @@ free_all_functions(void) long_u todo = 1; long_u used; - /* Clean up the call stack. */ + /* Clean up the current_funccal chain and the funccal stack. */ while (current_funccal != NULL) { clear_tv(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 @@ -3578,27 +3607,6 @@ current_func_returned(void) return current_funccal->returned; } -/* - * 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; - - current_funccal = NULL; - return (void *)fc; -} - - void -restore_funccal(void *vfc) -{ - funccall_T *fc = (funccall_T *)vfc; - - current_funccal = fc; -} - int free_unref_funccal(int copyID, int testing) { @@ -3702,25 +3710,6 @@ get_funccal_args_var() } /* - * Clear the current_funccal and return the old value. - * Caller is expected to invoke restore_current_funccal(). - */ - void * -clear_current_funccal() -{ - funccall_T *f = current_funccal; - - current_funccal = NULL; - return f; -} - - void -restore_current_funccal(void *f) -{ - current_funccal = f; -} - -/* * List function variables, if there is a function. */ void diff --git a/src/version.c b/src/version.c --- a/src/version.c +++ b/src/version.c @@ -793,6 +793,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ /**/ + 475, +/**/ 474, /**/ 473,