# HG changeset patch # User Bram Moolenaar # Date 1605634204 -3600 # Node ID 54219df706b5a8ab1cb16a5e8ad3cc98614a8d4d # Parent 6a8b703b33a3f9d269b3a10f441f73b64b54bdf4 patch 8.2.2001: Vim9: :def function does not apply 'maxfuncdepth' Commit: https://github.com/vim/vim/commit/0ba48e8c2741bd65d547fe6bf1d9873f411b25b4 Author: Bram Moolenaar Date: Tue Nov 17 18:23:19 2020 +0100 patch 8.2.2001: Vim9: :def function does not apply 'maxfuncdepth' Problem: Vim9: :def function does not apply 'maxfuncdepth'. Solution: Use 'maxfuncdepth'. (issue https://github.com/vim/vim/issues/7313) diff --git a/src/proto/userfunc.pro b/src/proto/userfunc.pro --- a/src/proto/userfunc.pro +++ b/src/proto/userfunc.pro @@ -14,6 +14,10 @@ ufunc_T *find_func(char_u *name, int is_ int func_is_global(ufunc_T *ufunc); int func_name_refcount(char_u *name); void copy_func(char_u *lambda, char_u *global); +int funcdepth_increment(void); +void funcdepth_decrement(void); +int funcdepth_get(void); +void funcdepth_restore(int depth); int call_user_func_check(ufunc_T *fp, int argcount, typval_T *argvars, typval_T *rettv, funcexe_T *funcexe, dict_T *selfdict); void save_funccal(funccal_entry_T *entry); void restore_funccal(void); diff --git a/src/testdir/test_vim9_func.vim b/src/testdir/test_vim9_func.vim --- a/src/testdir/test_vim9_func.vim +++ b/src/testdir/test_vim9_func.vim @@ -49,6 +49,36 @@ def TestCompilingError() call delete('XTest_compile_error') enddef +def CallRecursive(n: number): number + return CallRecursive(n + 1) +enddef + +def CallMapRecursive(l: list): number + return map(l, {_, v -> CallMapRecursive([v])})[0] +enddef + +def Test_funcdepth_error() + set maxfuncdepth=10 + + var caught = false + try + CallRecursive(1) + catch /E132:/ + caught = true + endtry + assert_true(caught) + + caught = false + try + CallMapRecursive([1]) + catch /E132:/ + caught = true + endtry + assert_true(caught) + + set maxfuncdepth& +enddef + def ReturnString(): string return 'string' enddef diff --git a/src/userfunc.c b/src/userfunc.c --- a/src/userfunc.c +++ b/src/userfunc.c @@ -1373,6 +1373,50 @@ failed: func_clear_free(fp, TRUE); } +static int funcdepth = 0; + +/* + * Increment the function call depth count. + * Return FAIL when going over 'maxfuncdepth'. + * Otherwise return OK, must call funcdepth_decrement() later! + */ + int +funcdepth_increment(void) +{ + if (funcdepth >= p_mfd) + { + emsg(_("E132: Function call depth is higher than 'maxfuncdepth'")); + return FAIL; + } + ++funcdepth; + return OK; +} + + void +funcdepth_decrement(void) +{ + --funcdepth; +} + +/* + * Get the current function call depth. + */ + int +funcdepth_get(void) +{ + return funcdepth; +} + +/* + * Restore the function call depth. This is for cases where there is no + * garantee funcdepth_decrement() can be called exactly the same number of + * times as funcdepth_increment(). + */ + void +funcdepth_restore(int depth) +{ + funcdepth = depth; +} /* * Call a user function. @@ -1391,7 +1435,6 @@ call_user_func( funccall_T *fc; int save_did_emsg; int default_arg_err = FALSE; - static int depth = 0; dictitem_T *v; int fixvar_idx = 0; // index in fixvar[] int i; @@ -1406,15 +1449,13 @@ call_user_func( #endif ESTACK_CHECK_DECLARATION - // If depth of calling is getting too high, don't execute the function - if (depth >= p_mfd) + // If depth of calling is getting too high, don't execute the function. + if (funcdepth_increment() == FAIL) { - emsg(_("E132: Function call depth is higher than 'maxfuncdepth'")); rettv->v_type = VAR_NUMBER; rettv->vval.v_number = -1; return; } - ++depth; line_breakcheck(); // check for CTRL-C hit @@ -1437,7 +1478,7 @@ call_user_func( { // Execute the function, possibly compiling it first. call_def_function(fp, argcount, argvars, funcexe->partial, rettv); - --depth; + funcdepth_decrement(); current_funccal = fc->caller; free_funccal(fc); return; @@ -1783,8 +1824,7 @@ call_user_func( } did_emsg |= save_did_emsg; - --depth; - + funcdepth_decrement(); cleanup_function_call(fc); } diff --git a/src/version.c b/src/version.c --- a/src/version.c +++ b/src/version.c @@ -751,6 +751,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ /**/ + 2001, +/**/ 2000, /**/ 1999, diff --git a/src/vim9execute.c b/src/vim9execute.c --- a/src/vim9execute.c +++ b/src/vim9execute.c @@ -227,6 +227,10 @@ call_dfunc(int cdf_idx, int argcount_arg == FAIL) return FAIL; + // If depth of calling is getting too high, don't execute the function. + if (funcdepth_increment() == FAIL) + return FAIL; + // Move the vararg-list to below the missing optional arguments. if (vararg_count > 0 && arg_to_add > 0) *STACK_TV_BOT(arg_to_add - 1) = *STACK_TV_BOT(-1); @@ -503,6 +507,7 @@ func_return(ectx_T *ectx) ectx->ec_stack.ga_len = top + 1; *STACK_TV_BOT(-1) = *STACK_TV(idx); + funcdepth_decrement(); return OK; } @@ -835,6 +840,7 @@ call_def_function( cmdmod_T save_cmdmod; int restore_cmdmod = FALSE; int trylevel_at_start = trylevel; + int orig_funcdepth; // Get pointer to item in the stack. #define STACK_TV(idx) (((typval_T *)ectx.ec_stack.ga_data) + idx) @@ -870,11 +876,19 @@ call_def_function( } } + // If depth of calling is getting too high, don't execute the function. + orig_funcdepth = funcdepth_get(); + if (funcdepth_increment() == FAIL) + return FAIL; + CLEAR_FIELD(ectx); ectx.ec_dfunc_idx = ufunc->uf_dfunc_idx; ga_init2(&ectx.ec_stack, sizeof(typval_T), 500); if (ga_grow(&ectx.ec_stack, 20) == FAIL) + { + funcdepth_decrement(); return FAIL; + } ga_init2(&ectx.ec_trystack, sizeof(trycmd_T), 10); ga_init2(&ectx.ec_funcrefs, sizeof(partial_T *), 10); @@ -2941,6 +2955,7 @@ failed_early: if (ret != OK && did_emsg == did_emsg_before) semsg(_(e_unknown_error_while_executing_str), printable_func_name(ufunc)); + funcdepth_restore(orig_funcdepth); return ret; }