Mirror of the Vim source from https://github.com/vim/vim
Revisão | b8f778dda1a13342c57a4b304425ab1dbce99805 (tree) |
---|---|
Hora | 2020-02-27 05:30:04 |
Autor | Bram Moolenaar <Bram@vim....> |
Commiter | Bram Moolenaar |
patch 8.2.0323: Vim9: calling a function that is defined later is slow
Commit: https://github.com/vim/vim/commit/7eeefd4a395fe3d7c7a2a0879467cf7ed4c29fe6
Author: Bram Moolenaar <Bram@vim.org>
Date: Wed Feb 26 21:24:23 2020 +0100
@@ -222,6 +222,38 @@ | ||
222 | 222 | enddef |
223 | 223 | |
224 | 224 | |
225 | +def FuncWithForwardCall(): string | |
226 | + return DefinedLater("yes") | |
227 | +enddef | |
228 | + | |
229 | +def DefinedLater(arg: string): string | |
230 | + return arg | |
231 | +enddef | |
232 | + | |
233 | +def Test_disassemble_update_instr() | |
234 | + let res = execute('disass FuncWithForwardCall') | |
235 | + assert_match('FuncWithForwardCall.*' | |
236 | + \ .. 'return DefinedLater("yes").*' | |
237 | + \ .. '\d PUSHS "yes".*' | |
238 | + \ .. '\d UCALL DefinedLater(argc 1).*' | |
239 | + \ .. '\d CHECKTYPE string stack\[-1].*' | |
240 | + \ .. '\d RETURN.*' | |
241 | + \, res) | |
242 | + | |
243 | + " Calling the function will change UCALL into the faster DCALL | |
244 | + assert_equal('yes', FuncWithForwardCall()) | |
245 | + | |
246 | + res = execute('disass FuncWithForwardCall') | |
247 | + assert_match('FuncWithForwardCall.*' | |
248 | + \ .. 'return DefinedLater("yes").*' | |
249 | + \ .. '\d PUSHS "yes".*' | |
250 | + \ .. '\d DCALL DefinedLater(argc 1).*' | |
251 | + \ .. '\d CHECKTYPE string stack\[-1].*' | |
252 | + \ .. '\d RETURN.*' | |
253 | + \, res) | |
254 | +enddef | |
255 | + | |
256 | + | |
225 | 257 | def FuncWithDefault(arg: string = 'default'): string |
226 | 258 | return arg |
227 | 259 | enddef |
@@ -212,6 +212,19 @@ | ||
212 | 212 | return a:arg |
213 | 213 | endfunc |
214 | 214 | |
215 | +def FuncWithForwardCall() | |
216 | + return DefinedEvenLater("yes") | |
217 | +enddef | |
218 | + | |
219 | +def DefinedEvenLater(arg: string): string | |
220 | + return arg | |
221 | +enddef | |
222 | + | |
223 | +def Test_error_in_nested_function() | |
224 | + " Error in called function requires unwinding the call stack. | |
225 | + assert_fails('call FuncWithForwardCall()', 'E1029') | |
226 | +enddef | |
227 | + | |
215 | 228 | def Test_return_type_wrong() |
216 | 229 | CheckScriptFailure(['def Func(): number', 'return "a"', 'enddef'], 'expected number but got string') |
217 | 230 | CheckScriptFailure(['def Func(): string', 'return 1', 'enddef'], 'expected string but got number') |
@@ -739,6 +739,8 @@ | ||
739 | 739 | static int included_patches[] = |
740 | 740 | { /* Add new patch number below this line */ |
741 | 741 | /**/ |
742 | + 323, | |
743 | +/**/ | |
742 | 744 | 322, |
743 | 745 | /**/ |
744 | 746 | 321, |
@@ -267,9 +267,10 @@ | ||
267 | 267 | |
268 | 268 | /* |
269 | 269 | * Execute a user defined function. |
270 | + * "iptr" can be used to replace the instruction with a more efficient one. | |
270 | 271 | */ |
271 | 272 | static int |
272 | -call_ufunc(ufunc_T *ufunc, int argcount, ectx_T *ectx) | |
273 | +call_ufunc(ufunc_T *ufunc, int argcount, ectx_T *ectx, isn_T *iptr) | |
273 | 274 | { |
274 | 275 | typval_T argvars[MAX_FUNC_ARGS]; |
275 | 276 | funcexe_T funcexe; |
@@ -277,8 +278,17 @@ | ||
277 | 278 | int idx; |
278 | 279 | |
279 | 280 | if (ufunc->uf_dfunc_idx >= 0) |
280 | - // The function has been compiled, can call it quickly. | |
281 | + { | |
282 | + // The function has been compiled, can call it quickly. For a function | |
283 | + // that was defined later: we can call it directly next time. | |
284 | + if (iptr != NULL) | |
285 | + { | |
286 | + iptr->isn_type = ISN_DCALL; | |
287 | + iptr->isn_arg.dfunc.cdf_idx = ufunc->uf_dfunc_idx; | |
288 | + iptr->isn_arg.dfunc.cdf_argcount = argcount; | |
289 | + } | |
281 | 290 | return call_dfunc(ufunc->uf_dfunc_idx, argcount, ectx); |
291 | + } | |
282 | 292 | |
283 | 293 | if (call_prepare(argcount, argvars, ectx) == FAIL) |
284 | 294 | return FAIL; |
@@ -305,10 +315,11 @@ | ||
305 | 315 | /* |
306 | 316 | * Execute a function by "name". |
307 | 317 | * This can be a builtin function or a user function. |
318 | + * "iptr" can be used to replace the instruction with a more efficient one. | |
308 | 319 | * Returns FAIL if not found without an error message. |
309 | 320 | */ |
310 | 321 | static int |
311 | -call_by_name(char_u *name, int argcount, ectx_T *ectx) | |
322 | +call_by_name(char_u *name, int argcount, ectx_T *ectx, isn_T *iptr) | |
312 | 323 | { |
313 | 324 | ufunc_T *ufunc; |
314 | 325 |
@@ -325,7 +336,7 @@ | ||
325 | 336 | |
326 | 337 | ufunc = find_func(name, NULL); |
327 | 338 | if (ufunc != NULL) |
328 | - return call_ufunc(ufunc, argcount, ectx); | |
339 | + return call_ufunc(ufunc, argcount, ectx, iptr); | |
329 | 340 | |
330 | 341 | return FAIL; |
331 | 342 | } |
@@ -341,12 +352,12 @@ | ||
341 | 352 | partial_T *pt = tv->vval.v_partial; |
342 | 353 | |
343 | 354 | if (pt->pt_func != NULL) |
344 | - return call_ufunc(pt->pt_func, argcount, ectx); | |
355 | + return call_ufunc(pt->pt_func, argcount, ectx, NULL); | |
345 | 356 | name = pt->pt_name; |
346 | 357 | } |
347 | 358 | else |
348 | 359 | name = tv->vval.v_string; |
349 | - if (call_by_name(name, argcount, ectx) == FAIL) | |
360 | + if (call_by_name(name, argcount, ectx, NULL) == FAIL) | |
350 | 361 | { |
351 | 362 | if (called_emsg == called_emsg_before) |
352 | 363 | semsg(_(e_unknownfunc), name); |
@@ -372,13 +383,14 @@ | ||
372 | 383 | /* |
373 | 384 | * Execute a function by "name". |
374 | 385 | * This can be a builtin function, user function or a funcref. |
386 | + * "iptr" can be used to replace the instruction with a more efficient one. | |
375 | 387 | */ |
376 | 388 | static int |
377 | -call_eval_func(char_u *name, int argcount, ectx_T *ectx) | |
389 | +call_eval_func(char_u *name, int argcount, ectx_T *ectx, isn_T *iptr) | |
378 | 390 | { |
379 | 391 | int called_emsg_before = called_emsg; |
380 | 392 | |
381 | - if (call_by_name(name, argcount, ectx) == FAIL | |
393 | + if (call_by_name(name, argcount, ectx, iptr) == FAIL | |
382 | 394 | && called_emsg == called_emsg_before) |
383 | 395 | { |
384 | 396 | // "name" may be a variable that is a funcref or partial |
@@ -983,7 +995,7 @@ | ||
983 | 995 | |
984 | 996 | SOURCING_LNUM = iptr->isn_lnum; |
985 | 997 | if (call_eval_func(cufunc->cuf_name, |
986 | - cufunc->cuf_argcount, &ectx) == FAIL) | |
998 | + cufunc->cuf_argcount, &ectx, iptr) == FAIL) | |
987 | 999 | goto failed; |
988 | 1000 | } |
989 | 1001 | break; |
@@ -1558,10 +1570,7 @@ | ||
1558 | 1570 | |
1559 | 1571 | tv = STACK_TV_BOT(-1); |
1560 | 1572 | if (check_not_string(tv) == FAIL) |
1561 | - { | |
1562 | - --ectx.ec_stack.ga_len; | |
1563 | 1573 | goto failed; |
1564 | - } | |
1565 | 1574 | (void)tv_get_number_chk(tv, &error); |
1566 | 1575 | if (error) |
1567 | 1576 | goto failed; |
@@ -1627,6 +1636,10 @@ | ||
1627 | 1636 | ret = OK; |
1628 | 1637 | |
1629 | 1638 | failed: |
1639 | + // When failed need to unwind the call stack. | |
1640 | + while (ectx.ec_frame != initial_frame_ptr) | |
1641 | + func_return(&ectx); | |
1642 | + | |
1630 | 1643 | for (idx = 0; idx < ectx.ec_stack.ga_len; ++idx) |
1631 | 1644 | clear_tv(STACK_TV(idx)); |
1632 | 1645 | vim_free(ectx.ec_stack.ga_data); |