# HG changeset patch # User Bram Moolenaar # Date 1662495302 -7200 # Node ID 01408b56f093d52c756878bbf545460b3a43711c # Parent 1dcd041dd253b5645b4784b31f90e8fa664e3da1 patch 9.0.0399: using :defer in expression funcref not tested Commit: https://github.com/vim/vim/commit/98aff658d5f97629d7c3a9fccac6c79fc9c6029d Author: Bram Moolenaar Date: Tue Sep 6 21:02:35 2022 +0100 patch 9.0.0399: using :defer in expression funcref not tested Problem: Using :defer in expression funcref not tested. Solution: Add a test. Fix uncovered problems. diff --git a/src/proto/vim9execute.pro b/src/proto/vim9execute.pro --- a/src/proto/vim9execute.pro +++ b/src/proto/vim9execute.pro @@ -4,6 +4,8 @@ void update_has_breakpoint(ufunc_T *ufun void funcstack_check_refcount(funcstack_T *funcstack); int set_ref_in_funcstacks(int copyID); int in_def_function(void); +ectx_T *clear_currrent_ectx(void); +void restore_current_ectx(ectx_T *ectx); int add_defer_function(char_u *name, int argcount, typval_T *argvars); char_u *char_from_string(char_u *str, varnumber_T index); char_u *string_slice(char_u *str, varnumber_T first, varnumber_T last, int exclusive); diff --git a/src/testdir/test_user_func.vim b/src/testdir/test_user_func.vim --- a/src/testdir/test_user_func.vim +++ b/src/testdir/test_user_func.vim @@ -625,5 +625,37 @@ func Test_defer_quitall() call assert_false(filereadable('XQuitallTwo')) endfunc +func FuncIndex(idx, val) + call writefile([a:idx .. ': ' .. a:val], 'Xentry' .. a:idx, 'D') + return a:val == 'c' +endfunc + +def DefIndex(idx: number, val: string): bool + call writefile([idx .. ': ' .. val], 'Xentry' .. idx, 'D') + return val == 'c' +enddef + +def Test_defer_in_funcref() + assert_equal(2, indexof(['a', 'b', 'c'], function('g:FuncIndex'))) + assert_false(filereadable('Xentry0')) + assert_false(filereadable('Xentry1')) + assert_false(filereadable('Xentry2')) + + assert_equal(2, indexof(['a', 'b', 'c'], g:DefIndex)) + assert_false(filereadable('Xentry0')) + assert_false(filereadable('Xentry1')) + assert_false(filereadable('Xentry2')) + + assert_equal(2, indexof(['a', 'b', 'c'], function('g:DefIndex'))) + assert_false(filereadable('Xentry0')) + assert_false(filereadable('Xentry1')) + assert_false(filereadable('Xentry2')) + + assert_equal(2, indexof(['a', 'b', 'c'], funcref(g:DefIndex))) + assert_false(filereadable('Xentry0')) + assert_false(filereadable('Xentry1')) + assert_false(filereadable('Xentry2')) +enddef + " vim: shiftwidth=2 sts=2 expandtab diff --git a/src/userfunc.c b/src/userfunc.c --- a/src/userfunc.c +++ b/src/userfunc.c @@ -2593,6 +2593,7 @@ call_user_func( dict_T *selfdict) // Dictionary for "self" { sctx_T save_current_sctx; + ectx_T *save_current_ectx; int using_sandbox = FALSE; int save_sticky_cmdmod_flags = sticky_cmdmod_flags; funccall_T *fc; @@ -2669,9 +2670,9 @@ call_user_func( islambda = fp->uf_flags & FC_LAMBDA; /* - * Note about using fc->fc_fixvar[]: This is an array of FIXVAR_CNT variables - * with names up to VAR_SHORT_LEN long. This avoids having to alloc/free - * each argument variable and saves a lot of time. + * Note about using fc->fc_fixvar[]: This is an array of FIXVAR_CNT + * variables with names up to VAR_SHORT_LEN long. This avoids having to + * alloc/free each argument variable and saves a lot of time. */ /* * Init l: variables. @@ -2885,6 +2886,11 @@ call_user_func( // "legacy" does not apply to commands in the function sticky_cmdmod_flags = 0; + // If called from a compiled :def function the execution context must be + // hidden, any deferred functions need to be added to the function being + // executed here. + save_current_ectx = clear_currrent_ectx(); + save_current_sctx = current_sctx; current_sctx = fp->uf_script_ctx; save_did_emsg = did_emsg; @@ -2974,6 +2980,8 @@ call_user_func( ESTACK_CHECK_NOW estack_pop(); current_sctx = save_current_sctx; + restore_current_ectx(save_current_ectx); + #ifdef FEAT_PROFILE if (do_profiling == PROF_YES) script_prof_restore(&profile_info.pi_wait_start); diff --git a/src/version.c b/src/version.c --- a/src/version.c +++ b/src/version.c @@ -704,6 +704,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ /**/ + 399, +/**/ 398, /**/ 397, diff --git a/src/vim9execute.c b/src/vim9execute.c --- a/src/vim9execute.c +++ b/src/vim9execute.c @@ -860,6 +860,27 @@ in_def_function(void) } /* + * Clear "current_ectx" and return the previous value. To be used when calling + * a user function. + */ + ectx_T * +clear_currrent_ectx(void) +{ + ectx_T *r = current_ectx; + + current_ectx = NULL; + return r; +} + + void +restore_current_ectx(ectx_T *ectx) +{ + if (current_ectx != NULL) + iemsg("Restoring current_ectx while it is not NULL"); + current_ectx = ectx; +} + +/* * Add an entry for a deferred function call to the currently executing * function. * Return the list or NULL when failed. @@ -5335,7 +5356,7 @@ call_def_function( if (idx < 0) { semsg(NGETTEXT(e_one_argument_too_few, e_nr_arguments_too_few, - -idx), -idx); + -idx), -idx); goto failed_early; }