Problem: Using freed memory with lambda.
Solution: Do not free lines early, keep them until the expression is
finished.
void
fill_evalarg_from_eap(evalarg_T *evalarg, exarg_T *eap, int skip)
{
- CLEAR_FIELD(*evalarg);
+ init_evalarg(evalarg);
evalarg->eval_flags = skip ? 0 : EVAL_EVALUATE;
if (eap != NULL)
{
// Advanced to the next line, "arg" no longer points into the previous
// line.
- VIM_CLEAR(evalarg->eval_tofree_cmdline);
-
+ evalarg->eval_using_cmdline = FALSE;
return skipwhite(line);
}
return p;
}
+/*
+ * Initialize "evalarg" for use.
+ */
+ void
+init_evalarg(evalarg_T *evalarg)
+{
+ CLEAR_POINTER(evalarg);
+ ga_init2(&evalarg->eval_tofree_ga, sizeof(char_u *), 20);
+}
+
/*
* After using "evalarg" filled from "eap": free the memory.
*/
evalarg->eval_tofree = NULL;
}
- VIM_CLEAR(evalarg->eval_tofree_cmdline);
+ ga_clear_strings(&evalarg->eval_tofree_ga);
VIM_CLEAR(evalarg->eval_tofree_lambda);
}
}
if (evalarg == NULL)
{
- CLEAR_FIELD(local_evalarg);
+ init_evalarg(&local_evalarg);
evalarg_used = &local_evalarg;
}
orig_flags = evalarg_used->eval_flags;
if (evalarg == NULL)
{
- CLEAR_FIELD(local_evalarg);
+ init_evalarg(&local_evalarg);
evalarg_used = &local_evalarg;
}
orig_flags = evalarg_used->eval_flags;
if (evalarg == NULL)
{
- CLEAR_FIELD(local_evalarg);
+ init_evalarg(&local_evalarg);
evalarg_used = &local_evalarg;
}
orig_flags = evalarg_used->eval_flags;
// Passed to an eval() function to enable evaluation.
EXTERN evalarg_T EVALARG_EVALUATE
# ifdef DO_INIT
- = {EVAL_EVALUATE, 0, NULL, NULL, NULL, NULL, {0, 0, 0, 0, NULL},
- {0, 0, 0, 0, NULL}, NULL, NULL, NULL}
+ = {EVAL_EVALUATE, 0, NULL, NULL, NULL, NULL, GA_EMPTY, GA_EMPTY, NULL,
+ {0, 0, (int)sizeof(char_u *), 20, NULL}, 0, NULL}
# endif
;
#endif
void set_context_for_expression(expand_T *xp, char_u *arg, cmdidx_T cmdidx);
int pattern_match(char_u *pat, char_u *text, int ic);
char_u *skipwhite_and_linebreak(char_u *arg, evalarg_T *evalarg);
+void init_evalarg(evalarg_T *evalarg);
void clear_evalarg(evalarg_T *evalarg, exarg_T *eap);
int eval0(char_u *arg, typval_T *rettv, exarg_T *eap, evalarg_T *evalarg);
int eval1(char_u **arg, typval_T *rettv, evalarg_T *evalarg);
// pointer to the last line obtained with getsourceline()
char_u *eval_tofree;
- // pointer to the last line of an inline function
- char_u *eval_tofree_cmdline;
+ // array with lines of an inline function
+ garray_T eval_tofree_ga;
+
+ // set when "arg" points into the last entry of "eval_tofree_ga"
+ int eval_using_cmdline;
// pointer to the lines concatenated for a lambda.
char_u *eval_tofree_lambda;
CheckScriptSuccess(lines)
enddef
+def Test_lambda_in_reduce_line_break()
+ # this was using freed memory
+ var lines =<< trim END
+ vim9script
+ const result: dict<number> =
+ ['Bob', 'Sam', 'Cat', 'Bob', 'Cat', 'Cat']
+ ->reduce((acc, val) => {
+ if has_key(acc, val)
+ acc[val] += 1
+ return acc
+ else
+ acc[val] = 1
+ return acc
+ endif
+ }, {})
+ assert_equal({Bob: 2, Sam: 1, Cat: 3}, result)
+ END
+ CheckScriptSuccess(lines)
+enddef
+
" Default arg and varargs
def MyDefVarargs(one: string, two = 'foo', ...rest: list<string>): string
var res = one .. ',' .. two
if (cmdline != NULL)
{
+ garray_T *tfgap = &evalarg->eval_tofree_ga;
+
// Something comes after the "}".
*arg = eap.nextcmd;
// "arg" points into cmdline, need to keep the line and free it later.
- vim_free(evalarg->eval_tofree_cmdline);
- evalarg->eval_tofree_cmdline = cmdline;
+ if (ga_grow(tfgap, 1) == OK)
+ {
+ ((char_u **)(tfgap->ga_data))[tfgap->ga_len++] = cmdline;
+ evalarg->eval_using_cmdline = TRUE;
+ }
}
else
*arg = (char_u *)"";
return;
}
- CLEAR_FIELD(evalarg);
+ init_evalarg(&evalarg);
evalarg.eval_flags = eap->skip ? 0 : EVAL_EVALUATE;
if (eap->skip)
static int included_patches[] =
{ /* Add new patch number below this line */
+/**/
+ 3560,
/**/
3559,
/**/
ufunc_T *ufunc;
evalarg_T evalarg;
- CLEAR_FIELD(evalarg);
+ init_evalarg(&evalarg);
evalarg.eval_flags = EVAL_EVALUATE;
evalarg.eval_cctx = cctx;
compile_def_function(ufunc, FALSE, CT_NONE, cctx);
#endif
- // evalarg.eval_tofree_cmdline may have a copy of the last line and "*arg"
- // points into it. Point to the original line to avoid a dangling pointer.
- if (evalarg.eval_tofree_cmdline != NULL)
+ // The last entry in evalarg.eval_tofree_ga is a copy of the last line and
+ // "*arg" may point into it. Point into the original line to avoid a
+ // dangling pointer.
+ if (evalarg.eval_using_cmdline)
{
- size_t off = *arg - evalarg.eval_tofree_cmdline;
+ garray_T *gap = &evalarg.eval_tofree_ga;
+ size_t off = *arg - ((char_u **)gap->ga_data)[gap->ga_len - 1];
*arg = ((char_u **)cctx->ctx_ufunc->uf_lines.ga_data)[cctx->ctx_lnum]
+ off;
{
evalarg_T evalarg;
- CLEAR_FIELD(evalarg);
+ init_evalarg(&evalarg);
evalarg.eval_cctx = cctx;
skip_expr(arg, &evalarg);
+ clear_evalarg(&evalarg, NULL);
}
/*