patch 8.2.2001: Vim9: :def function does not apply 'maxfuncdepth'
Problem: Vim9: :def function does not apply 'maxfuncdepth'. Solution: Use 'maxfuncdepth'. (issue #7313)
This commit is contained in:
		| @ -14,6 +14,10 @@ ufunc_T *find_func(char_u *name, int is_global, cctx_T *cctx); | |||||||
| int func_is_global(ufunc_T *ufunc); | int func_is_global(ufunc_T *ufunc); | ||||||
| int func_name_refcount(char_u *name); | int func_name_refcount(char_u *name); | ||||||
| void copy_func(char_u *lambda, char_u *global); | 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); | 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 save_funccal(funccal_entry_T *entry); | ||||||
| void restore_funccal(void); | void restore_funccal(void); | ||||||
|  | |||||||
| @ -49,6 +49,36 @@ def TestCompilingError() | |||||||
|   call delete('XTest_compile_error') |   call delete('XTest_compile_error') | ||||||
| enddef | enddef | ||||||
|  |  | ||||||
|  | def CallRecursive(n: number): number | ||||||
|  |   return CallRecursive(n + 1) | ||||||
|  | enddef | ||||||
|  |  | ||||||
|  | def CallMapRecursive(l: list<number>): 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 | def ReturnString(): string | ||||||
|   return 'string' |   return 'string' | ||||||
| enddef | enddef | ||||||
|  | |||||||
| @ -1373,6 +1373,50 @@ failed: | |||||||
|     func_clear_free(fp, TRUE); |     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. |  * Call a user function. | ||||||
| @ -1391,7 +1435,6 @@ call_user_func( | |||||||
|     funccall_T	*fc; |     funccall_T	*fc; | ||||||
|     int		save_did_emsg; |     int		save_did_emsg; | ||||||
|     int		default_arg_err = FALSE; |     int		default_arg_err = FALSE; | ||||||
|     static int	depth = 0; |  | ||||||
|     dictitem_T	*v; |     dictitem_T	*v; | ||||||
|     int		fixvar_idx = 0;	// index in fixvar[] |     int		fixvar_idx = 0;	// index in fixvar[] | ||||||
|     int		i; |     int		i; | ||||||
| @ -1406,15 +1449,13 @@ call_user_func( | |||||||
| #endif | #endif | ||||||
|     ESTACK_CHECK_DECLARATION |     ESTACK_CHECK_DECLARATION | ||||||
|  |  | ||||||
|     // If depth of calling is getting too high, don't execute the function |     // If depth of calling is getting too high, don't execute the function. | ||||||
|     if (depth >= p_mfd) |     if (funcdepth_increment() == FAIL) | ||||||
|     { |     { | ||||||
| 	emsg(_("E132: Function call depth is higher than 'maxfuncdepth'")); |  | ||||||
| 	rettv->v_type = VAR_NUMBER; | 	rettv->v_type = VAR_NUMBER; | ||||||
| 	rettv->vval.v_number = -1; | 	rettv->vval.v_number = -1; | ||||||
| 	return; | 	return; | ||||||
|     } |     } | ||||||
|     ++depth; |  | ||||||
|  |  | ||||||
|     line_breakcheck();		// check for CTRL-C hit |     line_breakcheck();		// check for CTRL-C hit | ||||||
|  |  | ||||||
| @ -1437,7 +1478,7 @@ call_user_func( | |||||||
|     { |     { | ||||||
| 	// Execute the function, possibly compiling it first. | 	// Execute the function, possibly compiling it first. | ||||||
| 	call_def_function(fp, argcount, argvars, funcexe->partial, rettv); | 	call_def_function(fp, argcount, argvars, funcexe->partial, rettv); | ||||||
| 	--depth; | 	funcdepth_decrement(); | ||||||
| 	current_funccal = fc->caller; | 	current_funccal = fc->caller; | ||||||
| 	free_funccal(fc); | 	free_funccal(fc); | ||||||
| 	return; | 	return; | ||||||
| @ -1783,8 +1824,7 @@ call_user_func( | |||||||
|     } |     } | ||||||
|  |  | ||||||
|     did_emsg |= save_did_emsg; |     did_emsg |= save_did_emsg; | ||||||
|     --depth; |     funcdepth_decrement(); | ||||||
|  |  | ||||||
|     cleanup_function_call(fc); |     cleanup_function_call(fc); | ||||||
| } | } | ||||||
|  |  | ||||||
|  | |||||||
| @ -750,6 +750,8 @@ static char *(features[]) = | |||||||
|  |  | ||||||
| static int included_patches[] = | static int included_patches[] = | ||||||
| {   /* Add new patch number below this line */ | {   /* Add new patch number below this line */ | ||||||
|  | /**/ | ||||||
|  |     2001, | ||||||
| /**/ | /**/ | ||||||
|     2000, |     2000, | ||||||
| /**/ | /**/ | ||||||
|  | |||||||
| @ -227,6 +227,10 @@ call_dfunc(int cdf_idx, int argcount_arg, ectx_T *ectx) | |||||||
| 								       == FAIL) | 								       == FAIL) | ||||||
| 	return 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. |     // Move the vararg-list to below the missing optional arguments. | ||||||
|     if (vararg_count > 0 && arg_to_add > 0) |     if (vararg_count > 0 && arg_to_add > 0) | ||||||
| 	*STACK_TV_BOT(arg_to_add - 1) = *STACK_TV_BOT(-1); | 	*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; |     ectx->ec_stack.ga_len = top + 1; | ||||||
|     *STACK_TV_BOT(-1) = *STACK_TV(idx); |     *STACK_TV_BOT(-1) = *STACK_TV(idx); | ||||||
|  |  | ||||||
|  |     funcdepth_decrement(); | ||||||
|     return OK; |     return OK; | ||||||
| } | } | ||||||
|  |  | ||||||
| @ -835,6 +840,7 @@ call_def_function( | |||||||
|     cmdmod_T	save_cmdmod; |     cmdmod_T	save_cmdmod; | ||||||
|     int		restore_cmdmod = FALSE; |     int		restore_cmdmod = FALSE; | ||||||
|     int		trylevel_at_start = trylevel; |     int		trylevel_at_start = trylevel; | ||||||
|  |     int		orig_funcdepth; | ||||||
|  |  | ||||||
| // Get pointer to item in the stack. | // Get pointer to item in the stack. | ||||||
| #define STACK_TV(idx) (((typval_T *)ectx.ec_stack.ga_data) + idx) | #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); |     CLEAR_FIELD(ectx); | ||||||
|     ectx.ec_dfunc_idx = ufunc->uf_dfunc_idx; |     ectx.ec_dfunc_idx = ufunc->uf_dfunc_idx; | ||||||
|     ga_init2(&ectx.ec_stack, sizeof(typval_T), 500); |     ga_init2(&ectx.ec_stack, sizeof(typval_T), 500); | ||||||
|     if (ga_grow(&ectx.ec_stack, 20) == FAIL) |     if (ga_grow(&ectx.ec_stack, 20) == FAIL) | ||||||
|  |     { | ||||||
|  | 	funcdepth_decrement(); | ||||||
| 	return FAIL; | 	return FAIL; | ||||||
|  |     } | ||||||
|     ga_init2(&ectx.ec_trystack, sizeof(trycmd_T), 10); |     ga_init2(&ectx.ec_trystack, sizeof(trycmd_T), 10); | ||||||
|     ga_init2(&ectx.ec_funcrefs, sizeof(partial_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) |     if (ret != OK && did_emsg == did_emsg_before) | ||||||
| 	semsg(_(e_unknown_error_while_executing_str), | 	semsg(_(e_unknown_error_while_executing_str), | ||||||
| 						   printable_func_name(ufunc)); | 						   printable_func_name(ufunc)); | ||||||
|  |     funcdepth_restore(orig_funcdepth); | ||||||
|     return ret; |     return ret; | ||||||
| } | } | ||||||
|  |  | ||||||
|  | |||||||
		Reference in New Issue
	
	Block a user