From c96b7f5d2af241c5eb1589e9da3dc09e45355e65 Mon Sep 17 00:00:00 2001 From: Bram Moolenaar Date: Fri, 2 Dec 2022 15:58:38 +0000 Subject: patch 9.0.0990: callback name argument is changed by setqflist() 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 #11653) --- src/change.c | 2 ++ src/evalvars.c | 20 ++++++++------------ src/job.c | 43 +++++++++++++++++++++++-------------------- src/option.c | 2 ++ src/popupwin.c | 16 +++++++++++++++- src/quickfix.c | 4 ++++ src/testdir/test_quickfix.vim | 12 ++++++++++++ src/time.c | 2 ++ src/version.c | 2 ++ 9 files changed, 70 insertions(+), 33 deletions(-) diff --git a/src/change.c b/src/change.c index 7796d29207..dc68d3cf7c 100644 --- a/src/change.c +++ b/src/change.c @@ -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; diff --git a/src/evalvars.c b/src/evalvars.c index 20223c8fe8..31cc760bf0 100644 --- a/src/evalvars.c +++ b/src/evalvars.c @@ -4792,9 +4792,9 @@ 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) @@ -4802,7 +4802,7 @@ 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; @@ -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) diff --git a/src/job.c b/src/job.c index 259d49c3d0..ba5eec0f10 100644 --- a/src/job.c +++ b/src/job.c @@ -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); } @@ -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); } /* @@ -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); } diff --git a/src/option.c b/src/option.c index 816837d0af..07a9e52f3a 100644 --- a/src/option.c +++ b/src/option.c @@ -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 diff --git a/src/popupwin.c b/src/popupwin.c index 9bc31746ec..e487f0c225 100644 --- a/src/popupwin.c +++ b/src/popupwin.c @@ -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); } } @@ -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); @@ -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); } } } @@ -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; diff --git a/src/quickfix.c b/src/quickfix.c index 4c414e0ad9..ca5b35652c 100644 --- a/src/quickfix.c +++ b/src/quickfix.c @@ -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; } diff --git a/src/testdir/test_quickfix.vim b/src/testdir/test_quickfix.vim index e82e64757d..1620db5281 100644 --- a/src/testdir/test_quickfix.vim +++ b/src/testdir/test_quickfix.vim @@ -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 diff --git a/src/time.c b/src/time.c index 891a9f49ed..efdad690a4 100644 --- a/src/time.c +++ b/src/time.c @@ -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; } } diff --git a/src/version.c b/src/version.c index 6583d462a1..f9b1e6c220 100644 --- a/src/version.c +++ b/src/version.c @@ -695,6 +695,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ +/**/ + 990, /**/ 989, /**/ -- cgit v1.2.3