summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorBram Moolenaar <Bram@vim.org>2018-10-14 21:41:01 +0200
committerBram Moolenaar <Bram@vim.org>2018-10-14 21:41:01 +0200
commit27e80c885bcb5c5cf6a6462d71d6c81b06ba2451 (patch)
tree5f23ab7eeb2ddc833a739d7c0ac62c0dd3a2e863
parenta16bc5450359294f9d8585da9f74e5082108b7fb (diff)
patch 8.1.0475: memory not freed on exit when quit in autocmdv8.1.0475
Problem: Memory not freed on exit when quit in autocmd. Solution: Remember funccal stack when executing autocmd.
-rw-r--r--src/eval.c12
-rw-r--r--src/ex_cmds2.c6
-rw-r--r--src/fileio.c8
-rw-r--r--src/main.c20
-rw-r--r--src/proto/userfunc.pro6
-rw-r--r--src/structs.h10
-rw-r--r--src/userfunc.c71
-rw-r--r--src/version.c2
8 files changed, 68 insertions, 67 deletions
diff --git a/src/eval.c b/src/eval.c
index 8746d55563..7c462fb6a2 100644
--- 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;
+ funccal_entry_T funccal_entry;
- save_funccalp = save_funccal();
+ 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 writing)
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 writing)
}
/* 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
index 032ebe93b9..9a23f82055 100644
--- 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
index 4380067fd5..afc5157d9d 100644
--- 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
index 5cfec34998..d4ff370550 100644
--- 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
index 3149ec50a2..ac9bdc0066 100644
--- 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
index ad03cd4776..ebd94c3619 100644
--- 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
index f6e16fefd4..ec239c7710 100644
--- 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
index 519003036f..4523c71f79 100644
--- 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,