diff --git a/src/proto/vim9execute.pro b/src/proto/vim9execute.pro index d43b1e2ebd..bb3d414e7a 100644 --- a/src/proto/vim9execute.pro +++ b/src/proto/vim9execute.pro @@ -4,6 +4,8 @@ void update_has_breakpoint(ufunc_T *ufunc); 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 index d7cfae1e94..ecf90b61ab 100644 --- 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 index 801425df81..76ab6b9d86 100644 --- 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 index a734b1578a..025b5763ac 100644 --- a/src/version.c +++ b/src/version.c @@ -703,6 +703,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ +/**/ + 399, /**/ 398, /**/ diff --git a/src/vim9execute.c b/src/vim9execute.c index 40b23bdd20..46251545ed 100644 --- a/src/vim9execute.c +++ b/src/vim9execute.c @@ -859,6 +859,27 @@ in_def_function(void) return current_ectx != NULL; } +/* + * 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. @@ -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; }