aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorzeertzjq <zeertzjq@outlook.com>2023-04-14 09:11:37 +0800
committerzeertzjq <zeertzjq@outlook.com>2023-04-14 09:42:59 +0800
commit8e2903d2fe810cfa3be41fc1e7a4d8394c84cf11 (patch)
tree3a18b8a3e608394451044181ca4368133635027d /src
parentbd83b587b18bb6f2ac555a992fa5b7d907de7e79 (diff)
downloadrneovim-8e2903d2fe810cfa3be41fc1e7a4d8394c84cf11.tar.gz
rneovim-8e2903d2fe810cfa3be41fc1e7a4d8394c84cf11.tar.bz2
rneovim-8e2903d2fe810cfa3be41fc1e7a4d8394c84cf11.zip
vim-patch:8.2.1049: Vim9: leaking memory when using continuation line
Problem: Vim9: leaking memory when using continuation line. Solution: Keep a pointer to the continuation line in evalarg_T. Centralize checking for a next command. https://github.com/vim/vim/commit/b171fb179053fa631fec74911b5fb9374cb6a8a1 Omit eval_next_line(): Vim9 script only. vim-patch:8.2.1050: missing change in struct Problem: Missing change in struct. Solution: Add missing change. https://github.com/vim/vim/commit/65a8ed37f7bc61fbe5c612a7b0eb0dfc16ad3e11 Co-authored-by: Bram Moolenaar <Bram@vim.org>
Diffstat (limited to 'src')
-rw-r--r--src/nvim/digraph.c2
-rw-r--r--src/nvim/eval.c52
-rw-r--r--src/nvim/eval.h5
-rw-r--r--src/nvim/eval/userfunc.c4
-rw-r--r--src/nvim/eval/vars.c2
-rw-r--r--src/nvim/ex_cmds_defs.h1
-rw-r--r--src/nvim/ex_docmd.c3
-rw-r--r--src/nvim/ex_eval.c15
-rw-r--r--src/nvim/fold.c3
-rw-r--r--src/nvim/mapping.c2
-rw-r--r--src/nvim/ops.c2
-rw-r--r--src/nvim/path.c4
-rw-r--r--src/nvim/quickfix.c2
-rw-r--r--src/nvim/regexp.c2
-rw-r--r--src/nvim/statusline.c4
15 files changed, 62 insertions, 41 deletions
diff --git a/src/nvim/digraph.c b/src/nvim/digraph.c
index 7d6349e552..de3808e4f5 100644
--- a/src/nvim/digraph.c
+++ b/src/nvim/digraph.c
@@ -2203,7 +2203,7 @@ bool get_keymap_str(win_T *wp, char *fmt, char *buf, int len)
curwin = wp;
STRCPY(buf, "b:keymap_name"); // must be writable
emsg_skip++;
- s = p = eval_to_string(buf, NULL, false);
+ s = p = eval_to_string(buf, false);
emsg_skip--;
curbuf = old_curbuf;
curwin = old_curwin;
diff --git a/src/nvim/eval.c b/src/nvim/eval.c
index 4fe86c8399..a9c5ca46f3 100644
--- a/src/nvim/eval.c
+++ b/src/nvim/eval.c
@@ -698,7 +698,7 @@ void eval_patch(const char *const origfile, const char *const difffile, const ch
/// @param skip only parse, don't execute
///
/// @return true or false.
-int eval_to_bool(char *arg, bool *error, char **nextcmd, int skip)
+int eval_to_bool(char *arg, bool *error, exarg_T *eap, int skip)
{
typval_T tv;
bool retval = false;
@@ -706,7 +706,7 @@ int eval_to_bool(char *arg, bool *error, char **nextcmd, int skip)
if (skip) {
emsg_skip++;
}
- if (eval0(arg, &tv, nextcmd, skip ? NULL : &EVALARG_EVALUATE) == FAIL) {
+ if (eval0(arg, &tv, eap, skip ? NULL : &EVALARG_EVALUATE) == FAIL) {
*error = true;
} else {
*error = false;
@@ -817,13 +817,12 @@ bool eval_expr_to_bool(const typval_T *expr, bool *error)
/// Top level evaluation function, returning a string
///
/// @param[in] arg String to evaluate.
-/// @param nextcmd Pointer to the start of the next Ex command.
/// @param[in] skip If true, only do parsing to nextcmd without reporting
/// errors or actually evaluating anything.
///
/// @return [allocated] string result of evaluation or NULL in case of error or
/// when skipping.
-char *eval_to_string_skip(const char *arg, const char **nextcmd, const bool skip)
+char *eval_to_string_skip(char *arg, exarg_T *eap, const bool skip)
FUNC_ATTR_MALLOC FUNC_ATTR_NONNULL_ARG(1) FUNC_ATTR_WARN_UNUSED_RESULT
{
typval_T tv;
@@ -832,8 +831,7 @@ char *eval_to_string_skip(const char *arg, const char **nextcmd, const bool skip
if (skip) {
emsg_skip++;
}
- if (eval0((char *)arg, &tv, (char **)nextcmd, skip ? NULL : &EVALARG_EVALUATE)
- == FAIL || skip) {
+ if (eval0(arg, &tv, eap, skip ? NULL : &EVALARG_EVALUATE) == FAIL || skip) {
retval = NULL;
} else {
retval = xstrdup(tv_get_string(&tv));
@@ -863,13 +861,13 @@ int skip_expr(char **pp)
/// a Float to a String.
///
/// @return pointer to allocated memory, or NULL for failure.
-char *eval_to_string(char *arg, char **nextcmd, bool convert)
+char *eval_to_string(char *arg, bool convert)
{
typval_T tv;
char *retval;
garray_T ga;
- if (eval0(arg, &tv, nextcmd, &EVALARG_EVALUATE) == FAIL) {
+ if (eval0(arg, &tv, NULL, &EVALARG_EVALUATE) == FAIL) {
retval = NULL;
} else {
if (convert && tv.v_type == VAR_LIST) {
@@ -899,7 +897,7 @@ char *eval_to_string(char *arg, char **nextcmd, bool convert)
/// textlock.
///
/// @param use_sandbox when true, use the sandbox.
-char *eval_to_string_safe(char *arg, char **nextcmd, int use_sandbox)
+char *eval_to_string_safe(char *arg, int use_sandbox)
{
char *retval;
funccal_entry_T funccal_entry;
@@ -909,7 +907,7 @@ char *eval_to_string_safe(char *arg, char **nextcmd, int use_sandbox)
sandbox++;
}
textlock++;
- retval = eval_to_string(arg, nextcmd, false);
+ retval = eval_to_string(arg, false);
if (use_sandbox) {
sandbox--;
}
@@ -1778,7 +1776,7 @@ notify:
/// @param[out] *errp set to true for an error, false otherwise;
///
/// @return a pointer that holds the info. Null when there is an error.
-void *eval_for_line(const char *arg, bool *errp, char **nextcmdp, int skip)
+void *eval_for_line(const char *arg, bool *errp, exarg_T *eap, int skip)
{
forinfo_T *fi = xcalloc(1, sizeof(forinfo_T));
const char *expr;
@@ -1802,7 +1800,7 @@ void *eval_for_line(const char *arg, bool *errp, char **nextcmdp, int skip)
if (skip) {
emsg_skip++;
}
- if (eval0(skipwhite(expr + 2), &tv, nextcmdp, &evalarg) == OK) {
+ if (eval0(skipwhite(expr + 2), &tv, eap, &evalarg) == OK) {
*errp = false;
if (!skip) {
if (tv.v_type == VAR_LIST) {
@@ -2230,7 +2228,7 @@ static int eval_func(char **const arg, char *const name, const int name_len, typ
/// @param evalarg can be NULL, &EVALARG_EVALUATE or a pointer.
///
/// @return OK or FAIL.
-int eval0(char *arg, typval_T *rettv, char **nextcmd, evalarg_T *const evalarg)
+int eval0(char *arg, typval_T *rettv, exarg_T *eap, evalarg_T *const evalarg)
{
int ret;
char *p;
@@ -2238,6 +2236,9 @@ int eval0(char *arg, typval_T *rettv, char **nextcmd, evalarg_T *const evalarg)
const int called_emsg_before = called_emsg;
bool end_error = false;
+ if (evalarg != NULL) {
+ evalarg->eval_tofree = NULL;
+ }
p = skipwhite(arg);
ret = eval1(&p, rettv, evalarg);
@@ -2263,8 +2264,24 @@ int eval0(char *arg, typval_T *rettv, char **nextcmd, evalarg_T *const evalarg)
}
ret = FAIL;
}
- if (nextcmd != NULL) {
- *nextcmd = check_nextcmd(p);
+
+ if (eap != NULL) {
+ eap->nextcmd = check_nextcmd(p);
+ }
+
+ if (evalarg != NULL) {
+ if (eap != NULL) {
+ if (evalarg->eval_tofree != NULL) {
+ // We may need to keep the original command line, e.g. for
+ // ":let" it has the variable names. But we may also need the
+ // new one, "nextcmd" points into it. Keep both.
+ xfree(eap->cmdline_tofree);
+ eap->cmdline_tofree = *eap->cmdlinep;
+ *eap->cmdlinep = evalarg->eval_tofree;
+ }
+ } else {
+ xfree(evalarg->eval_tofree);
+ }
}
return ret;
@@ -6537,15 +6554,14 @@ static char *make_expanded_name(const char *in_start, char *expr_start, char *ex
}
char *retval = NULL;
- char *nextcmd = NULL;
*expr_start = NUL;
*expr_end = NUL;
char c1 = *in_end;
*in_end = NUL;
- char *temp_result = eval_to_string(expr_start + 1, &nextcmd, false);
- if (temp_result != NULL && nextcmd == NULL) {
+ char *temp_result = eval_to_string(expr_start + 1, false);
+ if (temp_result != NULL) {
retval = xmalloc(strlen(temp_result) + (size_t)(expr_start - in_start)
+ (size_t)(in_end - expr_end) + 1);
STRCPY(retval, in_start);
diff --git a/src/nvim/eval.h b/src/nvim/eval.h
index b29abe3d73..4f517551c6 100644
--- a/src/nvim/eval.h
+++ b/src/nvim/eval.h
@@ -273,6 +273,9 @@ typedef struct {
/// copied from exarg_T when "getline" is "getsourceline". Can be NULL.
void *eval_cookie; // argument for getline()
+
+ /// pointer to the line obtained with getsourceline()
+ char *eval_tofree;
} evalarg_T;
/// Flag for expression evaluation.
@@ -281,7 +284,7 @@ enum {
};
/// Passed to an eval() function to enable evaluation.
-EXTERN evalarg_T EVALARG_EVALUATE INIT(= { EVAL_EVALUATE, NULL });
+EXTERN evalarg_T EVALARG_EVALUATE INIT(= { EVAL_EVALUATE, NULL, NULL });
#ifdef INCLUDE_GENERATED_DECLARATIONS
# include "eval.h.generated.h"
diff --git a/src/nvim/eval/userfunc.c b/src/nvim/eval/userfunc.c
index d994a31039..fe0249ea3a 100644
--- a/src/nvim/eval/userfunc.c
+++ b/src/nvim/eval/userfunc.c
@@ -2957,7 +2957,7 @@ void ex_return(exarg_T *eap)
eap->nextcmd = NULL;
if ((*arg != NUL && *arg != '|' && *arg != '\n')
- && eval0(arg, &rettv, &eap->nextcmd, &evalarg) != FAIL) {
+ && eval0(arg, &rettv, eap, &evalarg) != FAIL) {
if (!eap->skip) {
returning = do_return(eap, false, true, &rettv);
} else {
@@ -3008,7 +3008,7 @@ void ex_call(exarg_T *eap)
// instead to skip to any following command, e.g. for:
// :if 0 | call dict.foo().bar() | endif.
emsg_skip++;
- if (eval0(eap->arg, &rettv, &eap->nextcmd, NULL) != FAIL) {
+ if (eval0(eap->arg, &rettv, eap, NULL) != FAIL) {
tv_clear(&rettv);
}
emsg_skip--;
diff --git a/src/nvim/eval/vars.c b/src/nvim/eval/vars.c
index 02e526d7b6..bad676500b 100644
--- a/src/nvim/eval/vars.c
+++ b/src/nvim/eval/vars.c
@@ -259,7 +259,7 @@ void ex_let(exarg_T *eap)
.eval_flags = eap->skip ? 0 : EVAL_EVALUATE,
.eval_cookie = eap->getline == getsourceline ? eap->cookie : NULL,
};
- i = eval0(expr, &rettv, &eap->nextcmd, &evalarg);
+ i = eval0(expr, &rettv, eap, &evalarg);
if (eap->skip) {
emsg_skip--;
}
diff --git a/src/nvim/ex_cmds_defs.h b/src/nvim/ex_cmds_defs.h
index 629aaf14cf..7932649114 100644
--- a/src/nvim/ex_cmds_defs.h
+++ b/src/nvim/ex_cmds_defs.h
@@ -185,6 +185,7 @@ struct exarg {
char *nextcmd; ///< next command (NULL if none)
char *cmd; ///< the name of the command (except for :make)
char **cmdlinep; ///< pointer to pointer of allocated cmdline
+ char *cmdline_tofree; ///< free later
cmdidx_T cmdidx; ///< the index for the command
uint32_t argt; ///< flags for the command
int skip; ///< don't execute the command, only parse it
diff --git a/src/nvim/ex_docmd.c b/src/nvim/ex_docmd.c
index fbdb1bc2d7..e8ad472da8 100644
--- a/src/nvim/ex_docmd.c
+++ b/src/nvim/ex_docmd.c
@@ -2328,6 +2328,7 @@ doend:
}
ex_nesting_level--;
+ xfree(ea.cmdline_tofree);
return ea.nextcmd;
}
@@ -4432,7 +4433,7 @@ static void ex_colorscheme(exarg_T *eap)
char *expr = xstrdup("g:colors_name");
emsg_off++;
- char *p = eval_to_string(expr, NULL, false);
+ char *p = eval_to_string(expr, false);
emsg_off--;
xfree(expr);
diff --git a/src/nvim/ex_eval.c b/src/nvim/ex_eval.c
index 33b315c179..27f012a4ab 100644
--- a/src/nvim/ex_eval.c
+++ b/src/nvim/ex_eval.c
@@ -796,7 +796,7 @@ void ex_eval(exarg_T *eap)
.eval_flags = eap->skip ? 0 : EVAL_EVALUATE,
.eval_cookie = eap->getline == getsourceline ? eap->cookie : NULL,
};
- if (eval0(eap->arg, &tv, &eap->nextcmd, &evalarg) == OK) {
+ if (eval0(eap->arg, &tv, eap, &evalarg) == OK) {
tv_clear(&tv);
}
}
@@ -815,7 +815,7 @@ void ex_if(exarg_T *eap)
int skip = CHECK_SKIP;
bool error;
- int result = eval_to_bool(eap->arg, &error, &eap->nextcmd, skip);
+ int result = eval_to_bool(eap->arg, &error, eap, skip);
if (!skip && !error) {
if (result) {
@@ -910,7 +910,7 @@ void ex_else(exarg_T *eap)
if (skip && *eap->arg != '"' && ends_excmd(*eap->arg)) {
semsg(_(e_invexpr2), eap->arg);
} else {
- result = eval_to_bool(eap->arg, &error, &eap->nextcmd, skip);
+ result = eval_to_bool(eap->arg, &error, eap, skip);
}
// When throwing error exceptions, we want to throw always the first
@@ -957,7 +957,7 @@ void ex_while(exarg_T *eap)
int skip = CHECK_SKIP;
if (eap->cmdidx == CMD_while) {
// ":while bool-expr"
- result = eval_to_bool(eap->arg, &error, &eap->nextcmd, skip);
+ result = eval_to_bool(eap->arg, &error, eap, skip);
} else {
void *fi;
@@ -969,7 +969,7 @@ void ex_while(exarg_T *eap)
error = false;
} else {
// Evaluate the argument and get the info in a structure.
- fi = eval_for_line(eap->arg, &error, &eap->nextcmd, skip);
+ fi = eval_for_line(eap->arg, &error, eap, skip);
cstack->cs_forinfo[cstack->cs_idx] = fi;
}
@@ -1128,12 +1128,11 @@ void ex_endwhile(exarg_T *eap)
/// Handle ":throw expr"
void ex_throw(exarg_T *eap)
{
- const char *arg = eap->arg;
+ char *arg = eap->arg;
char *value;
if (*arg != NUL && *arg != '|' && *arg != '\n') {
- value = eval_to_string_skip(arg, (const char **)&eap->nextcmd,
- (bool)eap->skip);
+ value = eval_to_string_skip(arg, eap, eap->skip);
} else {
emsg(_(e_argreq));
value = NULL;
diff --git a/src/nvim/fold.c b/src/nvim/fold.c
index 71984e806d..a0869b54c9 100644
--- a/src/nvim/fold.c
+++ b/src/nvim/fold.c
@@ -1749,7 +1749,8 @@ char *get_foldtext(win_T *wp, linenr_T lnum, linenr_T lnume, foldinfo_T foldinfo
curbuf = wp->w_buffer;
emsg_silent++; // handle exceptions, but don't display errors
- text = eval_to_string_safe(wp->w_p_fdt, NULL, was_set_insecurely(wp, "foldtext", OPT_LOCAL));
+ text = eval_to_string_safe(wp->w_p_fdt,
+ was_set_insecurely(wp, "foldtext", OPT_LOCAL));
emsg_silent--;
if (text == NULL || did_emsg) {
diff --git a/src/nvim/mapping.c b/src/nvim/mapping.c
index 19a2aca75e..c1565a84f5 100644
--- a/src/nvim/mapping.c
+++ b/src/nvim/mapping.c
@@ -1632,7 +1632,7 @@ char *eval_map_expr(mapblock_T *mp, int c)
api_clear_error(&err);
}
} else {
- p = eval_to_string(expr, NULL, false);
+ p = eval_to_string(expr, false);
xfree(expr);
}
expr_map_lock--;
diff --git a/src/nvim/ops.c b/src/nvim/ops.c
index 9aacfcad30..b2c0dd6c01 100644
--- a/src/nvim/ops.c
+++ b/src/nvim/ops.c
@@ -763,7 +763,7 @@ char *get_expr_line(void)
}
nested++;
- rv = eval_to_string(expr_copy, NULL, true);
+ rv = eval_to_string(expr_copy, true);
nested--;
xfree(expr_copy);
return rv;
diff --git a/src/nvim/path.c b/src/nvim/path.c
index 368f3feb27..8bd3303166 100644
--- a/src/nvim/path.c
+++ b/src/nvim/path.c
@@ -1371,7 +1371,7 @@ static int expand_backtick(garray_T *gap, char *pat, int flags)
char *cmd = xstrnsave(pat + 1, strlen(pat) - 2);
if (*cmd == '=') { // `={expr}`: Expand expression
- buffer = eval_to_string(cmd + 1, &p, true);
+ buffer = eval_to_string(cmd + 1, true);
} else {
buffer = get_cmd_output(cmd, NULL, (flags & EW_SILENT) ? kShellOptSilent : 0, NULL);
}
@@ -1662,7 +1662,7 @@ void simplify_filename(char *filename)
static char *eval_includeexpr(const char *const ptr, const size_t len)
{
set_vim_var_string(VV_FNAME, ptr, (ptrdiff_t)len);
- char *res = eval_to_string_safe(curbuf->b_p_inex, NULL,
+ char *res = eval_to_string_safe(curbuf->b_p_inex,
was_set_insecurely(curwin, "includeexpr", OPT_LOCAL));
set_vim_var_string(VV_FNAME, NULL, 0);
return res;
diff --git a/src/nvim/quickfix.c b/src/nvim/quickfix.c
index 38e05f408e..fdcdd71ceb 100644
--- a/src/nvim/quickfix.c
+++ b/src/nvim/quickfix.c
@@ -6958,7 +6958,7 @@ void ex_cexpr(exarg_T *eap)
// Evaluate the expression. When the result is a string or a list we can
// use it to fill the errorlist.
typval_T tv;
- if (eval0(eap->arg, &tv, &eap->nextcmd, &EVALARG_EVALUATE) == FAIL) {
+ if (eval0(eap->arg, &tv, eap, &EVALARG_EVALUATE) == FAIL) {
return;
}
diff --git a/src/nvim/regexp.c b/src/nvim/regexp.c
index 94796f0ed3..3ed32bf8af 100644
--- a/src/nvim/regexp.c
+++ b/src/nvim/regexp.c
@@ -1812,7 +1812,7 @@ static int vim_regsub_both(char *source, typval_T *expr, char *dest, int destlen
}
tv_clear(&rettv);
} else {
- eval_result[nested] = eval_to_string(source + 2, NULL, true);
+ eval_result[nested] = eval_to_string(source + 2, true);
}
nesting--;
diff --git a/src/nvim/statusline.c b/src/nvim/statusline.c
index 6c698f45be..05649e9b7f 100644
--- a/src/nvim/statusline.c
+++ b/src/nvim/statusline.c
@@ -985,7 +985,7 @@ int build_stl_str_hl(win_T *wp, char *out, size_t outlen, char *fmt, char *opt_n
};
set_var(S_LEN("g:statusline_winid"), &tv, false);
- usefmt = eval_to_string_safe(fmt + 2, NULL, use_sandbox);
+ usefmt = eval_to_string_safe(fmt + 2, use_sandbox);
if (usefmt == NULL) {
usefmt = fmt;
}
@@ -1457,7 +1457,7 @@ int build_stl_str_hl(win_T *wp, char *out, size_t outlen, char *fmt, char *opt_n
}
// Note: The result stored in `t` is unused.
- str = eval_to_string_safe(out_p, &t, use_sandbox);
+ str = eval_to_string_safe(out_p, use_sandbox);
curwin = save_curwin;
curbuf = save_curbuf;