Skip to content

Commit

Permalink
patch 9.0.0990: callback name argument is changed by setqflist()
Browse files Browse the repository at this point in the history
Problem:    Callback name argument is changed by setqflist().
Solution:   Use the expanded function name for the callback, do not store it
            in the argument. (closes vim#11653)
  • Loading branch information
brammool committed Dec 2, 2022
1 parent 9c8d12c commit c96b7f5
Show file tree
Hide file tree
Showing 9 changed files with 70 additions and 33 deletions.
2 changes: 2 additions & 0 deletions src/change.c
Original file line number Diff line number Diff line change
Expand Up @@ -259,6 +259,8 @@ f_listener_add(typval_T *argvars, typval_T *rettv)
buf->b_listener = lnr;

set_callback(&lnr->lr_callback, &callback);
if (callback.cb_free_name)
vim_free(callback.cb_name);

lnr->lr_id = ++next_listener_id;
rettv->vval.v_number = lnr->lr_id;
Expand Down
20 changes: 8 additions & 12 deletions src/evalvars.c
Original file line number Diff line number Diff line change
Expand Up @@ -4792,17 +4792,17 @@ f_setbufvar(typval_T *argvars, typval_T *rettv UNUSED)

/*
* Get a callback from "arg". It can be a Funcref or a function name.
* When "arg" is zero return an empty string.
* "cb_name" is not allocated.
* "cb_name" is set to NULL for an invalid argument.
* When "arg" is zero "res.cb_name" is set to an empty string.
* If "res.cb_name" is allocated then "res.cb_free_name" is set to TRUE.
* "res.cb_name" is set to NULL for an invalid argument.
*/
callback_T
get_callback(typval_T *arg)
{
callback_T res;
int r = OK;

res.cb_free_name = FALSE;
CLEAR_FIELD(res);
if (arg->v_type == VAR_PARTIAL && arg->vval.v_partial != NULL)
{
res.cb_partial = arg->vval.v_partial;
Expand All @@ -4811,25 +4811,21 @@ get_callback(typval_T *arg)
}
else
{
res.cb_partial = NULL;
if (arg->v_type == VAR_STRING && arg->vval.v_string != NULL
&& isdigit(*arg->vval.v_string))
r = FAIL;
else if (arg->v_type == VAR_FUNC || arg->v_type == VAR_STRING)
{
res.cb_name = arg->vval.v_string;
if (arg->v_type == VAR_STRING)
{
char_u *name;

name = get_scriptlocal_funcname(arg->vval.v_string);
char_u *name = get_scriptlocal_funcname(arg->vval.v_string);
if (name != NULL)
{
vim_free(arg->vval.v_string);
arg->vval.v_string = name;
res.cb_name = name;
res.cb_free_name = TRUE;
}
}

res.cb_name = arg->vval.v_string;
func_ref(res.cb_name);
}
else if (arg->v_type == VAR_NUMBER && arg->vval.v_number == 0)
Expand Down
43 changes: 23 additions & 20 deletions src/job.c
Original file line number Diff line number Diff line change
Expand Up @@ -74,32 +74,31 @@ clear_job_options(jobopt_T *opt)
CLEAR_POINTER(opt);
}

static void
unref_job_callback(callback_T *cb)
{
if (cb->cb_partial != NULL)
partial_unref(cb->cb_partial);
else if (cb->cb_name != NULL)
{
func_unref(cb->cb_name);
if (cb->cb_free_name)
vim_free(cb->cb_name);
}
}

/*
* Free any members of a jobopt_T.
*/
void
free_job_options(jobopt_T *opt)
{
if (opt->jo_callback.cb_partial != NULL)
partial_unref(opt->jo_callback.cb_partial);
else if (opt->jo_callback.cb_name != NULL)
func_unref(opt->jo_callback.cb_name);
if (opt->jo_out_cb.cb_partial != NULL)
partial_unref(opt->jo_out_cb.cb_partial);
else if (opt->jo_out_cb.cb_name != NULL)
func_unref(opt->jo_out_cb.cb_name);
if (opt->jo_err_cb.cb_partial != NULL)
partial_unref(opt->jo_err_cb.cb_partial);
else if (opt->jo_err_cb.cb_name != NULL)
func_unref(opt->jo_err_cb.cb_name);
if (opt->jo_close_cb.cb_partial != NULL)
partial_unref(opt->jo_close_cb.cb_partial);
else if (opt->jo_close_cb.cb_name != NULL)
func_unref(opt->jo_close_cb.cb_name);
if (opt->jo_exit_cb.cb_partial != NULL)
partial_unref(opt->jo_exit_cb.cb_partial);
else if (opt->jo_exit_cb.cb_name != NULL)
func_unref(opt->jo_exit_cb.cb_name);
unref_job_callback(&opt->jo_callback);
unref_job_callback(&opt->jo_out_cb);
unref_job_callback(&opt->jo_err_cb);
unref_job_callback(&opt->jo_close_cb);
unref_job_callback(&opt->jo_exit_cb);

if (opt->jo_env != NULL)
dict_unref(opt->jo_env);
}
Expand Down Expand Up @@ -1687,6 +1686,8 @@ f_prompt_setcallback(typval_T *argvars, typval_T *rettv UNUSED)

free_callback(&buf->b_prompt_callback);
set_callback(&buf->b_prompt_callback, &callback);
if (callback.cb_free_name)
vim_free(callback.cb_name);
}

/*
Expand Down Expand Up @@ -1714,6 +1715,8 @@ f_prompt_setinterrupt(typval_T *argvars, typval_T *rettv UNUSED)

free_callback(&buf->b_prompt_interrupt);
set_callback(&buf->b_prompt_interrupt, &callback);
if (callback.cb_free_name)
vim_free(callback.cb_name);
}


Expand Down
2 changes: 2 additions & 0 deletions src/option.c
Original file line number Diff line number Diff line change
Expand Up @@ -7370,6 +7370,8 @@ option_set_callback_func(char_u *optval UNUSED, callback_T *optcb UNUSED)

free_callback(optcb);
set_callback(optcb, &cb);
if (cb.cb_free_name)
vim_free(cb.cb_name);
free_tv(tv);

// when using Vim9 style "import.funcname" it needs to be expanded to
Expand Down
16 changes: 15 additions & 1 deletion src/popupwin.c
Original file line number Diff line number Diff line change
Expand Up @@ -444,7 +444,13 @@ popup_add_timeout(win_T *wp, int time, int close)
if (get_lambda_tv_and_compile(&ptr, &tv, FALSE, &EVALARG_EVALUATE) == OK)
{
wp->w_popup_timer = create_timer(time, 0);
wp->w_popup_timer->tr_callback = get_callback(&tv);
callback_T cb = get_callback(&tv);
if (cb.cb_name != NULL && !cb.cb_free_name)
{
cb.cb_name = vim_strsave(cb.cb_name);
cb.cb_free_name = TRUE;
}
wp->w_popup_timer->tr_callback = cb;
clear_tv(&tv);
}
}
Expand Down Expand Up @@ -961,6 +967,8 @@ apply_general_options(win_T *wp, dict_T *dict)
{
free_callback(&wp->w_filter_cb);
set_callback(&wp->w_filter_cb, &callback);
if (callback.cb_free_name)
vim_free(callback.cb_name);
}
}
nr = dict_get_bool(dict, "mapping", -1);
Expand Down Expand Up @@ -990,6 +998,8 @@ apply_general_options(win_T *wp, dict_T *dict)
{
free_callback(&wp->w_close_cb);
set_callback(&wp->w_close_cb, &callback);
if (callback.cb_free_name)
vim_free(callback.cb_name);
}
}
}
Expand Down Expand Up @@ -2229,7 +2239,11 @@ popup_create(typval_T *argvars, typval_T *rettv, create_type_T type)
tv.vval.v_string = (char_u *)"popup_filter_menu";
callback = get_callback(&tv);
if (callback.cb_name != NULL)
{
set_callback(&wp->w_filter_cb, &callback);
if (callback.cb_free_name)
vim_free(callback.cb_name);
}

wp->w_p_wrap = 0;
wp->w_popup_flags |= POPF_CURSORLINE;
Expand Down
4 changes: 4 additions & 0 deletions src/quickfix.c
Original file line number Diff line number Diff line change
Expand Up @@ -7633,7 +7633,11 @@ qf_setprop_qftf(qf_info_T *qi UNUSED, qf_list_T *qfl, dictitem_T *di)
free_callback(&qfl->qf_qftf_cb);
cb = get_callback(&di->di_tv);
if (cb.cb_name != NULL && *cb.cb_name != NUL)
{
set_callback(&qfl->qf_qftf_cb, &cb);
if (cb.cb_free_name)
vim_free(cb.cb_name);
}

return OK;
}
Expand Down
12 changes: 12 additions & 0 deletions src/testdir/test_quickfix.vim
Original file line number Diff line number Diff line change
Expand Up @@ -6387,5 +6387,17 @@ func Test_info_line_with_space()
call setqflist([], 'f')
endfunc

func s:QfTf(_)
endfunc

func Test_setqflist_cb_arg()
" This was changing the callback name in the dictionary.
let d = #{quickfixtextfunc: 's:QfTf'}
call setqflist([], 'a', d)
call assert_equal('s:QfTf', d.quickfixtextfunc)

call setqflist([], 'f')
endfunc


" vim: shiftwidth=2 sts=2 expandtab
2 changes: 2 additions & 0 deletions src/time.c
Original file line number Diff line number Diff line change
Expand Up @@ -908,6 +908,8 @@ f_timer_start(typval_T *argvars, typval_T *rettv)
else
{
set_callback(&timer->tr_callback, &callback);
if (callback.cb_free_name)
vim_free(callback.cb_name);
rettv->vval.v_number = (varnumber_T)timer->tr_id;
}
}
Expand Down
2 changes: 2 additions & 0 deletions src/version.c
Original file line number Diff line number Diff line change
Expand Up @@ -695,6 +695,8 @@ static char *(features[]) =

static int included_patches[] =
{ /* Add new patch number below this line */
/**/
990,
/**/
989,
/**/
Expand Down

0 comments on commit c96b7f5

Please sign in to comment.