diff options
author | Bram Moolenaar <Bram@vim.org> | 2020-02-26 21:24:23 +0100 |
---|---|---|
committer | Bram Moolenaar <Bram@vim.org> | 2020-02-26 21:24:23 +0100 |
commit | 7eeefd4a395fe3d7c7a2a0879467cf7ed4c29fe6 (patch) | |
tree | a7d77697ae53255e35510b686f3ba2584803eb3a | |
parent | b35efa5ed040162f5c988c71dfc1159045e47585 (diff) |
patch 8.2.0323: Vim9: calling a function that is defined later is slowv8.2.0323
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.
-rw-r--r-- | src/testdir/test_vim9_disassemble.vim | 32 | ||||
-rw-r--r-- | src/testdir/test_vim9_script.vim | 13 | ||||
-rw-r--r-- | src/version.c | 2 | ||||
-rw-r--r-- | src/vim9execute.c | 37 |
4 files changed, 72 insertions, 12 deletions
diff --git a/src/testdir/test_vim9_disassemble.vim b/src/testdir/test_vim9_disassemble.vim index 3758697171..9196184a6f 100644 --- a/src/testdir/test_vim9_disassemble.vim +++ b/src/testdir/test_vim9_disassemble.vim @@ -222,6 +222,38 @@ def Test_disassemble_call() 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 return arg enddef diff --git a/src/testdir/test_vim9_script.vim b/src/testdir/test_vim9_script.vim index 53ee5119c8..f1b21d45e3 100644 --- a/src/testdir/test_vim9_script.vim +++ b/src/testdir/test_vim9_script.vim @@ -212,6 +212,19 @@ func DefinedLater(arg) return a:arg 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() CheckScriptFailure(['def Func(): number', 'return "a"', 'enddef'], 'expected number but got string') CheckScriptFailure(['def Func(): string', 'return 1', 'enddef'], 'expected string but got number') diff --git a/src/version.c b/src/version.c index 258cab24f6..f95526c0a9 100644 --- a/src/version.c +++ b/src/version.c @@ -739,6 +739,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ /**/ + 323, +/**/ 322, /**/ 321, diff --git a/src/vim9execute.c b/src/vim9execute.c index 4f22654754..9ecb8657e6 100644 --- a/src/vim9execute.c +++ b/src/vim9execute.c @@ -267,9 +267,10 @@ call_bfunc(int func_idx, int argcount, ectx_T *ectx) /* * Execute a user defined function. + * "iptr" can be used to replace the instruction with a more efficient one. */ 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]; funcexe_T funcexe; @@ -277,8 +278,17 @@ call_ufunc(ufunc_T *ufunc, int argcount, ectx_T *ectx) int idx; 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); + } if (call_prepare(argcount, argvars, ectx) == FAIL) return FAIL; @@ -305,10 +315,11 @@ call_ufunc(ufunc_T *ufunc, int argcount, ectx_T *ectx) /* * Execute a function by "name". * 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. */ 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; @@ -325,7 +336,7 @@ call_by_name(char_u *name, int argcount, ectx_T *ectx) ufunc = find_func(name, NULL); if (ufunc != NULL) - return call_ufunc(ufunc, argcount, ectx); + return call_ufunc(ufunc, argcount, ectx, iptr); return FAIL; } @@ -341,12 +352,12 @@ call_partial(typval_T *tv, int argcount, ectx_T *ectx) partial_T *pt = tv->vval.v_partial; 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; } else 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) semsg(_(e_unknownfunc), name); @@ -372,13 +383,14 @@ store_var(char_u *name, typval_T *tv) /* * Execute a function by "name". * 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 -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; - if (call_by_name(name, argcount, ectx) == FAIL + if (call_by_name(name, argcount, ectx, iptr) == FAIL && called_emsg == called_emsg_before) { // "name" may be a variable that is a funcref or partial @@ -983,7 +995,7 @@ call_def_function( SOURCING_LNUM = iptr->isn_lnum; if (call_eval_func(cufunc->cuf_name, - cufunc->cuf_argcount, &ectx) == FAIL) + cufunc->cuf_argcount, &ectx, iptr) == FAIL) goto failed; } break; @@ -1558,10 +1570,7 @@ call_def_function( tv = STACK_TV_BOT(-1); if (check_not_string(tv) == FAIL) - { - --ectx.ec_stack.ga_len; goto failed; - } (void)tv_get_number_chk(tv, &error); if (error) goto failed; @@ -1627,6 +1636,10 @@ done: ret = OK; 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) clear_tv(STACK_TV(idx)); vim_free(ectx.ec_stack.ga_data); |