mirror of
https://github.com/vim/vim.git
synced 2025-09-23 03:43:49 -04:00
patch 8.2.0323: Vim9: calling a function that is defined later is slow
Problem: Vim9: calling a function that is defined later is slow. Solution: Once the function is found update the instruction so it can be called directly.
This commit is contained in:
@@ -222,6 +222,38 @@ def Test_disassemble_call()
|
|||||||
enddef
|
enddef
|
||||||
|
|
||||||
|
|
||||||
|
def FuncWithForwardCall(): string
|
||||||
|
return DefinedLater("yes")
|
||||||
|
enddef
|
||||||
|
|
||||||
|
def DefinedLater(arg: string): string
|
||||||
|
return arg
|
||||||
|
enddef
|
||||||
|
|
||||||
|
def Test_disassemble_update_instr()
|
||||||
|
let res = execute('disass FuncWithForwardCall')
|
||||||
|
assert_match('FuncWithForwardCall.*'
|
||||||
|
\ .. 'return DefinedLater("yes").*'
|
||||||
|
\ .. '\d PUSHS "yes".*'
|
||||||
|
\ .. '\d UCALL DefinedLater(argc 1).*'
|
||||||
|
\ .. '\d CHECKTYPE string stack\[-1].*'
|
||||||
|
\ .. '\d RETURN.*'
|
||||||
|
\, res)
|
||||||
|
|
||||||
|
" Calling the function will change UCALL into the faster DCALL
|
||||||
|
assert_equal('yes', FuncWithForwardCall())
|
||||||
|
|
||||||
|
res = execute('disass FuncWithForwardCall')
|
||||||
|
assert_match('FuncWithForwardCall.*'
|
||||||
|
\ .. 'return DefinedLater("yes").*'
|
||||||
|
\ .. '\d PUSHS "yes".*'
|
||||||
|
\ .. '\d DCALL DefinedLater(argc 1).*'
|
||||||
|
\ .. '\d CHECKTYPE string stack\[-1].*'
|
||||||
|
\ .. '\d RETURN.*'
|
||||||
|
\, res)
|
||||||
|
enddef
|
||||||
|
|
||||||
|
|
||||||
def FuncWithDefault(arg: string = 'default'): string
|
def FuncWithDefault(arg: string = 'default'): string
|
||||||
return arg
|
return arg
|
||||||
enddef
|
enddef
|
||||||
|
@@ -212,6 +212,19 @@ func DefinedLater(arg)
|
|||||||
return a:arg
|
return a:arg
|
||||||
endfunc
|
endfunc
|
||||||
|
|
||||||
|
def FuncWithForwardCall()
|
||||||
|
return DefinedEvenLater("yes")
|
||||||
|
enddef
|
||||||
|
|
||||||
|
def DefinedEvenLater(arg: string): string
|
||||||
|
return arg
|
||||||
|
enddef
|
||||||
|
|
||||||
|
def Test_error_in_nested_function()
|
||||||
|
" Error in called function requires unwinding the call stack.
|
||||||
|
assert_fails('call FuncWithForwardCall()', 'E1029')
|
||||||
|
enddef
|
||||||
|
|
||||||
def Test_return_type_wrong()
|
def Test_return_type_wrong()
|
||||||
CheckScriptFailure(['def Func(): number', 'return "a"', 'enddef'], 'expected number but got string')
|
CheckScriptFailure(['def Func(): number', 'return "a"', 'enddef'], 'expected number but got string')
|
||||||
CheckScriptFailure(['def Func(): string', 'return 1', 'enddef'], 'expected string but got number')
|
CheckScriptFailure(['def Func(): string', 'return 1', 'enddef'], 'expected string but got number')
|
||||||
|
@@ -738,6 +738,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 */
|
||||||
|
/**/
|
||||||
|
323,
|
||||||
/**/
|
/**/
|
||||||
322,
|
322,
|
||||||
/**/
|
/**/
|
||||||
|
@@ -267,9 +267,10 @@ call_bfunc(int func_idx, int argcount, ectx_T *ectx)
|
|||||||
|
|
||||||
/*
|
/*
|
||||||
* Execute a user defined function.
|
* Execute a user defined function.
|
||||||
|
* "iptr" can be used to replace the instruction with a more efficient one.
|
||||||
*/
|
*/
|
||||||
static int
|
static int
|
||||||
call_ufunc(ufunc_T *ufunc, int argcount, ectx_T *ectx)
|
call_ufunc(ufunc_T *ufunc, int argcount, ectx_T *ectx, isn_T *iptr)
|
||||||
{
|
{
|
||||||
typval_T argvars[MAX_FUNC_ARGS];
|
typval_T argvars[MAX_FUNC_ARGS];
|
||||||
funcexe_T funcexe;
|
funcexe_T funcexe;
|
||||||
@@ -277,8 +278,17 @@ call_ufunc(ufunc_T *ufunc, int argcount, ectx_T *ectx)
|
|||||||
int idx;
|
int idx;
|
||||||
|
|
||||||
if (ufunc->uf_dfunc_idx >= 0)
|
if (ufunc->uf_dfunc_idx >= 0)
|
||||||
// The function has been compiled, can call it quickly.
|
{
|
||||||
|
// The function has been compiled, can call it quickly. For a function
|
||||||
|
// that was defined later: we can call it directly next time.
|
||||||
|
if (iptr != NULL)
|
||||||
|
{
|
||||||
|
iptr->isn_type = ISN_DCALL;
|
||||||
|
iptr->isn_arg.dfunc.cdf_idx = ufunc->uf_dfunc_idx;
|
||||||
|
iptr->isn_arg.dfunc.cdf_argcount = argcount;
|
||||||
|
}
|
||||||
return call_dfunc(ufunc->uf_dfunc_idx, argcount, ectx);
|
return call_dfunc(ufunc->uf_dfunc_idx, argcount, ectx);
|
||||||
|
}
|
||||||
|
|
||||||
if (call_prepare(argcount, argvars, ectx) == FAIL)
|
if (call_prepare(argcount, argvars, ectx) == FAIL)
|
||||||
return FAIL;
|
return FAIL;
|
||||||
@@ -305,10 +315,11 @@ call_ufunc(ufunc_T *ufunc, int argcount, ectx_T *ectx)
|
|||||||
/*
|
/*
|
||||||
* Execute a function by "name".
|
* Execute a function by "name".
|
||||||
* This can be a builtin function or a user function.
|
* This can be a builtin function or a user function.
|
||||||
|
* "iptr" can be used to replace the instruction with a more efficient one.
|
||||||
* Returns FAIL if not found without an error message.
|
* Returns FAIL if not found without an error message.
|
||||||
*/
|
*/
|
||||||
static int
|
static int
|
||||||
call_by_name(char_u *name, int argcount, ectx_T *ectx)
|
call_by_name(char_u *name, int argcount, ectx_T *ectx, isn_T *iptr)
|
||||||
{
|
{
|
||||||
ufunc_T *ufunc;
|
ufunc_T *ufunc;
|
||||||
|
|
||||||
@@ -325,7 +336,7 @@ call_by_name(char_u *name, int argcount, ectx_T *ectx)
|
|||||||
|
|
||||||
ufunc = find_func(name, NULL);
|
ufunc = find_func(name, NULL);
|
||||||
if (ufunc != NULL)
|
if (ufunc != NULL)
|
||||||
return call_ufunc(ufunc, argcount, ectx);
|
return call_ufunc(ufunc, argcount, ectx, iptr);
|
||||||
|
|
||||||
return FAIL;
|
return FAIL;
|
||||||
}
|
}
|
||||||
@@ -341,12 +352,12 @@ call_partial(typval_T *tv, int argcount, ectx_T *ectx)
|
|||||||
partial_T *pt = tv->vval.v_partial;
|
partial_T *pt = tv->vval.v_partial;
|
||||||
|
|
||||||
if (pt->pt_func != NULL)
|
if (pt->pt_func != NULL)
|
||||||
return call_ufunc(pt->pt_func, argcount, ectx);
|
return call_ufunc(pt->pt_func, argcount, ectx, NULL);
|
||||||
name = pt->pt_name;
|
name = pt->pt_name;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
name = tv->vval.v_string;
|
name = tv->vval.v_string;
|
||||||
if (call_by_name(name, argcount, ectx) == FAIL)
|
if (call_by_name(name, argcount, ectx, NULL) == FAIL)
|
||||||
{
|
{
|
||||||
if (called_emsg == called_emsg_before)
|
if (called_emsg == called_emsg_before)
|
||||||
semsg(_(e_unknownfunc), name);
|
semsg(_(e_unknownfunc), name);
|
||||||
@@ -372,13 +383,14 @@ store_var(char_u *name, typval_T *tv)
|
|||||||
/*
|
/*
|
||||||
* Execute a function by "name".
|
* Execute a function by "name".
|
||||||
* This can be a builtin function, user function or a funcref.
|
* This can be a builtin function, user function or a funcref.
|
||||||
|
* "iptr" can be used to replace the instruction with a more efficient one.
|
||||||
*/
|
*/
|
||||||
static int
|
static int
|
||||||
call_eval_func(char_u *name, int argcount, ectx_T *ectx)
|
call_eval_func(char_u *name, int argcount, ectx_T *ectx, isn_T *iptr)
|
||||||
{
|
{
|
||||||
int called_emsg_before = called_emsg;
|
int called_emsg_before = called_emsg;
|
||||||
|
|
||||||
if (call_by_name(name, argcount, ectx) == FAIL
|
if (call_by_name(name, argcount, ectx, iptr) == FAIL
|
||||||
&& called_emsg == called_emsg_before)
|
&& called_emsg == called_emsg_before)
|
||||||
{
|
{
|
||||||
// "name" may be a variable that is a funcref or partial
|
// "name" may be a variable that is a funcref or partial
|
||||||
@@ -983,7 +995,7 @@ call_def_function(
|
|||||||
|
|
||||||
SOURCING_LNUM = iptr->isn_lnum;
|
SOURCING_LNUM = iptr->isn_lnum;
|
||||||
if (call_eval_func(cufunc->cuf_name,
|
if (call_eval_func(cufunc->cuf_name,
|
||||||
cufunc->cuf_argcount, &ectx) == FAIL)
|
cufunc->cuf_argcount, &ectx, iptr) == FAIL)
|
||||||
goto failed;
|
goto failed;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
@@ -1558,10 +1570,7 @@ call_def_function(
|
|||||||
|
|
||||||
tv = STACK_TV_BOT(-1);
|
tv = STACK_TV_BOT(-1);
|
||||||
if (check_not_string(tv) == FAIL)
|
if (check_not_string(tv) == FAIL)
|
||||||
{
|
|
||||||
--ectx.ec_stack.ga_len;
|
|
||||||
goto failed;
|
goto failed;
|
||||||
}
|
|
||||||
(void)tv_get_number_chk(tv, &error);
|
(void)tv_get_number_chk(tv, &error);
|
||||||
if (error)
|
if (error)
|
||||||
goto failed;
|
goto failed;
|
||||||
@@ -1627,6 +1636,10 @@ done:
|
|||||||
ret = OK;
|
ret = OK;
|
||||||
|
|
||||||
failed:
|
failed:
|
||||||
|
// When failed need to unwind the call stack.
|
||||||
|
while (ectx.ec_frame != initial_frame_ptr)
|
||||||
|
func_return(&ectx);
|
||||||
|
|
||||||
for (idx = 0; idx < ectx.ec_stack.ga_len; ++idx)
|
for (idx = 0; idx < ectx.ec_stack.ga_len; ++idx)
|
||||||
clear_tv(STACK_TV(idx));
|
clear_tv(STACK_TV(idx));
|
||||||
vim_free(ectx.ec_stack.ga_data);
|
vim_free(ectx.ec_stack.ga_data);
|
||||||
|
Reference in New Issue
Block a user