Mercurial > vim
comparison src/userfunc.c @ 18991:847cc7932c42 v8.2.0056
patch 8.2.0056: execution stack is incomplete and inefficient
Commit: https://github.com/vim/vim/commit/1a47ae32cdc19b0fd5a82e19fe5fddf45db1a506
Author: Bram Moolenaar <Bram@vim.org>
Date: Sun Dec 29 23:04:25 2019 +0100
patch 8.2.0056: execution stack is incomplete and inefficient
Problem: Execution stack is incomplete and inefficient.
Solution: Introduce a proper execution stack and use it instead of
sourcing_name/sourcing_lnum. Create a string only when used.
author | Bram Moolenaar <Bram@vim.org> |
---|---|
date | Sun, 29 Dec 2019 23:15:04 +0100 |
parents | af44ca0ddf49 |
children | dd9ab0674eec |
comparison
equal
deleted
inserted
replaced
18990:1219ae40b086 | 18991:847cc7932c42 |
---|---|
224 ((ufunc_T **)current_funccal->fc_funcs.ga_data) | 224 ((ufunc_T **)current_funccal->fc_funcs.ga_data) |
225 [current_funccal->fc_funcs.ga_len++] = fp; | 225 [current_funccal->fc_funcs.ga_len++] = fp; |
226 return OK; | 226 return OK; |
227 } | 227 } |
228 | 228 |
229 static void | |
230 set_ufunc_name(ufunc_T *fp, char_u *name) | |
231 { | |
232 STRCPY(fp->uf_name, name); | |
233 | |
234 if (name[0] == K_SPECIAL) | |
235 { | |
236 fp->uf_name_exp = alloc(STRLEN(name) + 3); | |
237 if (fp->uf_name_exp != NULL) | |
238 { | |
239 STRCPY(fp->uf_name_exp, "<SNR>"); | |
240 STRCAT(fp->uf_name_exp, fp->uf_name + 3); | |
241 } | |
242 } | |
243 } | |
244 | |
229 /* | 245 /* |
230 * Parse a lambda expression and get a Funcref from "*arg". | 246 * Parse a lambda expression and get a Funcref from "*arg". |
231 * Return OK or FAIL. Returns NOTDONE for dict or {expr}. | 247 * Return OK or FAIL. Returns NOTDONE for dict or {expr}. |
232 */ | 248 */ |
233 int | 249 int |
307 ((char_u **)(newlines.ga_data))[newlines.ga_len++] = p; | 323 ((char_u **)(newlines.ga_data))[newlines.ga_len++] = p; |
308 STRCPY(p, "return "); | 324 STRCPY(p, "return "); |
309 vim_strncpy(p + 7, s, e - s); | 325 vim_strncpy(p + 7, s, e - s); |
310 | 326 |
311 fp->uf_refcount = 1; | 327 fp->uf_refcount = 1; |
312 STRCPY(fp->uf_name, name); | 328 set_ufunc_name(fp, name); |
313 hash_add(&func_hashtab, UF2HIKEY(fp)); | 329 hash_add(&func_hashtab, UF2HIKEY(fp)); |
314 fp->uf_args = newargs; | 330 fp->uf_args = newargs; |
315 ga_init(&fp->uf_def_args); | 331 ga_init(&fp->uf_def_args); |
316 fp->uf_lines = newlines; | 332 fp->uf_lines = newlines; |
317 if (current_funccal != NULL && eval_lavars) | 333 if (current_funccal != NULL && eval_lavars) |
331 flags |= FC_SANDBOX; | 347 flags |= FC_SANDBOX; |
332 fp->uf_varargs = TRUE; | 348 fp->uf_varargs = TRUE; |
333 fp->uf_flags = flags; | 349 fp->uf_flags = flags; |
334 fp->uf_calls = 0; | 350 fp->uf_calls = 0; |
335 fp->uf_script_ctx = current_sctx; | 351 fp->uf_script_ctx = current_sctx; |
336 fp->uf_script_ctx.sc_lnum += sourcing_lnum - newlines.ga_len; | 352 fp->uf_script_ctx.sc_lnum += SOURCING_LNUM - newlines.ga_len; |
337 | 353 |
338 pt->pt_func = fp; | 354 pt->pt_func = fp; |
339 pt->pt_refcount = 1; | 355 pt->pt_refcount = 1; |
340 rettv->vval.v_partial = pt; | 356 rettv->vval.v_partial = pt; |
341 rettv->v_type = VAR_PARTIAL; | 357 rettv->v_type = VAR_PARTIAL; |
757 typval_T *rettv, // return value | 773 typval_T *rettv, // return value |
758 linenr_T firstline, // first line of range | 774 linenr_T firstline, // first line of range |
759 linenr_T lastline, // last line of range | 775 linenr_T lastline, // last line of range |
760 dict_T *selfdict) // Dictionary for "self" | 776 dict_T *selfdict) // Dictionary for "self" |
761 { | 777 { |
762 char_u *save_sourcing_name; | |
763 linenr_T save_sourcing_lnum; | |
764 sctx_T save_current_sctx; | 778 sctx_T save_current_sctx; |
765 int using_sandbox = FALSE; | 779 int using_sandbox = FALSE; |
766 funccall_T *fc; | 780 funccall_T *fc; |
767 int save_did_emsg; | 781 int save_did_emsg; |
768 int default_arg_err = FALSE; | 782 int default_arg_err = FALSE; |
772 int i; | 786 int i; |
773 int ai; | 787 int ai; |
774 int islambda = FALSE; | 788 int islambda = FALSE; |
775 char_u numbuf[NUMBUFLEN]; | 789 char_u numbuf[NUMBUFLEN]; |
776 char_u *name; | 790 char_u *name; |
777 size_t len; | |
778 #ifdef FEAT_PROFILE | 791 #ifdef FEAT_PROFILE |
779 proftime_T wait_start; | 792 proftime_T wait_start; |
780 proftime_T call_start; | 793 proftime_T call_start; |
781 int started_profiling = FALSE; | 794 int started_profiling = FALSE; |
782 #endif | 795 #endif |
946 } | 959 } |
947 } | 960 } |
948 | 961 |
949 // Don't redraw while executing the function. | 962 // Don't redraw while executing the function. |
950 ++RedrawingDisabled; | 963 ++RedrawingDisabled; |
951 save_sourcing_name = sourcing_name; | |
952 save_sourcing_lnum = sourcing_lnum; | |
953 sourcing_lnum = 1; | |
954 | 964 |
955 if (fp->uf_flags & FC_SANDBOX) | 965 if (fp->uf_flags & FC_SANDBOX) |
956 { | 966 { |
957 using_sandbox = TRUE; | 967 using_sandbox = TRUE; |
958 ++sandbox; | 968 ++sandbox; |
959 } | 969 } |
960 | 970 |
961 // need space for function name + ("function " + 3) or "[number]" | 971 estack_push_ufunc(ETYPE_UFUNC, fp, 1); |
962 len = (save_sourcing_name == NULL ? 0 : STRLEN(save_sourcing_name)) | 972 if (p_verbose >= 12) |
963 + STRLEN(fp->uf_name) + 20; | 973 { |
964 sourcing_name = alloc(len); | 974 ++no_wait_return; |
965 if (sourcing_name != NULL) | 975 verbose_enter_scroll(); |
966 { | 976 |
967 if (save_sourcing_name != NULL | 977 smsg(_("calling %s"), SOURCING_NAME); |
968 && STRNCMP(save_sourcing_name, "function ", 9) == 0) | 978 if (p_verbose >= 14) |
969 sprintf((char *)sourcing_name, "%s[%d]..", | 979 { |
970 save_sourcing_name, (int)save_sourcing_lnum); | 980 char_u buf[MSG_BUF_LEN]; |
971 else | 981 char_u numbuf2[NUMBUFLEN]; |
972 STRCPY(sourcing_name, "function "); | 982 char_u *tofree; |
973 cat_func_name(sourcing_name + STRLEN(sourcing_name), fp); | 983 char_u *s; |
974 | 984 |
975 if (p_verbose >= 12) | 985 msg_puts("("); |
976 { | 986 for (i = 0; i < argcount; ++i) |
977 ++no_wait_return; | 987 { |
978 verbose_enter_scroll(); | 988 if (i > 0) |
979 | 989 msg_puts(", "); |
980 smsg(_("calling %s"), sourcing_name); | 990 if (argvars[i].v_type == VAR_NUMBER) |
981 if (p_verbose >= 14) | 991 msg_outnum((long)argvars[i].vval.v_number); |
982 { | 992 else |
983 char_u buf[MSG_BUF_LEN]; | |
984 char_u numbuf2[NUMBUFLEN]; | |
985 char_u *tofree; | |
986 char_u *s; | |
987 | |
988 msg_puts("("); | |
989 for (i = 0; i < argcount; ++i) | |
990 { | 993 { |
991 if (i > 0) | 994 // Do not want errors such as E724 here. |
992 msg_puts(", "); | 995 ++emsg_off; |
993 if (argvars[i].v_type == VAR_NUMBER) | 996 s = tv2string(&argvars[i], &tofree, numbuf2, 0); |
994 msg_outnum((long)argvars[i].vval.v_number); | 997 --emsg_off; |
995 else | 998 if (s != NULL) |
996 { | 999 { |
997 // Do not want errors such as E724 here. | 1000 if (vim_strsize(s) > MSG_BUF_CLEN) |
998 ++emsg_off; | |
999 s = tv2string(&argvars[i], &tofree, numbuf2, 0); | |
1000 --emsg_off; | |
1001 if (s != NULL) | |
1002 { | 1001 { |
1003 if (vim_strsize(s) > MSG_BUF_CLEN) | 1002 trunc_string(s, buf, MSG_BUF_CLEN, MSG_BUF_LEN); |
1004 { | 1003 s = buf; |
1005 trunc_string(s, buf, MSG_BUF_CLEN, MSG_BUF_LEN); | |
1006 s = buf; | |
1007 } | |
1008 msg_puts((char *)s); | |
1009 vim_free(tofree); | |
1010 } | 1004 } |
1005 msg_puts((char *)s); | |
1006 vim_free(tofree); | |
1011 } | 1007 } |
1012 } | 1008 } |
1013 msg_puts(")"); | 1009 } |
1014 } | 1010 msg_puts(")"); |
1015 msg_puts("\n"); // don't overwrite this either | 1011 } |
1016 | 1012 msg_puts("\n"); // don't overwrite this either |
1017 verbose_leave_scroll(); | 1013 |
1018 --no_wait_return; | 1014 verbose_leave_scroll(); |
1019 } | 1015 --no_wait_return; |
1020 } | 1016 } |
1021 #ifdef FEAT_PROFILE | 1017 #ifdef FEAT_PROFILE |
1022 if (do_profiling == PROF_YES) | 1018 if (do_profiling == PROF_YES) |
1023 { | 1019 { |
1024 if (!fp->uf_profiling && has_profiling(FALSE, fp->uf_name, NULL)) | 1020 if (!fp->uf_profiling && has_profiling(FALSE, fp->uf_name, NULL)) |
1083 { | 1079 { |
1084 ++no_wait_return; | 1080 ++no_wait_return; |
1085 verbose_enter_scroll(); | 1081 verbose_enter_scroll(); |
1086 | 1082 |
1087 if (aborting()) | 1083 if (aborting()) |
1088 smsg(_("%s aborted"), sourcing_name); | 1084 smsg(_("%s aborted"), SOURCING_NAME); |
1089 else if (fc->rettv->v_type == VAR_NUMBER) | 1085 else if (fc->rettv->v_type == VAR_NUMBER) |
1090 smsg(_("%s returning #%ld"), sourcing_name, | 1086 smsg(_("%s returning #%ld"), SOURCING_NAME, |
1091 (long)fc->rettv->vval.v_number); | 1087 (long)fc->rettv->vval.v_number); |
1092 else | 1088 else |
1093 { | 1089 { |
1094 char_u buf[MSG_BUF_LEN]; | 1090 char_u buf[MSG_BUF_LEN]; |
1095 char_u numbuf2[NUMBUFLEN]; | 1091 char_u numbuf2[NUMBUFLEN]; |
1107 if (vim_strsize(s) > MSG_BUF_CLEN) | 1103 if (vim_strsize(s) > MSG_BUF_CLEN) |
1108 { | 1104 { |
1109 trunc_string(s, buf, MSG_BUF_CLEN, MSG_BUF_LEN); | 1105 trunc_string(s, buf, MSG_BUF_CLEN, MSG_BUF_LEN); |
1110 s = buf; | 1106 s = buf; |
1111 } | 1107 } |
1112 smsg(_("%s returning %s"), sourcing_name, s); | 1108 smsg(_("%s returning %s"), SOURCING_NAME, s); |
1113 vim_free(tofree); | 1109 vim_free(tofree); |
1114 } | 1110 } |
1115 } | 1111 } |
1116 msg_puts("\n"); // don't overwrite this either | 1112 msg_puts("\n"); // don't overwrite this either |
1117 | 1113 |
1118 verbose_leave_scroll(); | 1114 verbose_leave_scroll(); |
1119 --no_wait_return; | 1115 --no_wait_return; |
1120 } | 1116 } |
1121 | 1117 |
1122 vim_free(sourcing_name); | 1118 estack_pop(); |
1123 sourcing_name = save_sourcing_name; | |
1124 sourcing_lnum = save_sourcing_lnum; | |
1125 current_sctx = save_current_sctx; | 1119 current_sctx = save_current_sctx; |
1126 #ifdef FEAT_PROFILE | 1120 #ifdef FEAT_PROFILE |
1127 if (do_profiling == PROF_YES) | 1121 if (do_profiling == PROF_YES) |
1128 script_prof_restore(&wait_start); | 1122 script_prof_restore(&wait_start); |
1129 #endif | 1123 #endif |
1130 if (using_sandbox) | 1124 if (using_sandbox) |
1131 --sandbox; | 1125 --sandbox; |
1132 | 1126 |
1133 if (p_verbose >= 12 && sourcing_name != NULL) | 1127 if (p_verbose >= 12 && SOURCING_NAME != NULL) |
1134 { | 1128 { |
1135 ++no_wait_return; | 1129 ++no_wait_return; |
1136 verbose_enter_scroll(); | 1130 verbose_enter_scroll(); |
1137 | 1131 |
1138 smsg(_("continuing in %s"), sourcing_name); | 1132 smsg(_("continuing in %s"), SOURCING_NAME); |
1139 msg_puts("\n"); // don't overwrite this either | 1133 msg_puts("\n"); // don't overwrite this either |
1140 | 1134 |
1141 verbose_leave_scroll(); | 1135 verbose_leave_scroll(); |
1142 --no_wait_return; | 1136 --no_wait_return; |
1143 } | 1137 } |
1202 func_clear_items(ufunc_T *fp) | 1196 func_clear_items(ufunc_T *fp) |
1203 { | 1197 { |
1204 ga_clear_strings(&(fp->uf_args)); | 1198 ga_clear_strings(&(fp->uf_args)); |
1205 ga_clear_strings(&(fp->uf_def_args)); | 1199 ga_clear_strings(&(fp->uf_def_args)); |
1206 ga_clear_strings(&(fp->uf_lines)); | 1200 ga_clear_strings(&(fp->uf_lines)); |
1201 VIM_CLEAR(fp->uf_name_exp); | |
1207 #ifdef FEAT_PROFILE | 1202 #ifdef FEAT_PROFILE |
1208 vim_free(fp->uf_tml_count); | 1203 VIM_CLEAR(fp->uf_tml_count); |
1209 fp->uf_tml_count = NULL; | 1204 VIM_CLEAR(fp->uf_tml_total); |
1210 vim_free(fp->uf_tml_total); | 1205 VIM_CLEAR(fp->uf_tml_self); |
1211 fp->uf_tml_total = NULL; | |
1212 vim_free(fp->uf_tml_self); | |
1213 fp->uf_tml_self = NULL; | |
1214 #endif | 1206 #endif |
1215 } | 1207 } |
1216 | 1208 |
1217 /* | 1209 /* |
1218 * Free all things that a function contains. Does not free the function | 1210 * Free all things that a function contains. Does not free the function |
1734 | 1726 |
1735 msg_start(); | 1727 msg_start(); |
1736 if (indent) | 1728 if (indent) |
1737 msg_puts(" "); | 1729 msg_puts(" "); |
1738 msg_puts("function "); | 1730 msg_puts("function "); |
1739 if (fp->uf_name[0] == K_SPECIAL) | 1731 if (fp->uf_name_exp != NULL) |
1740 { | 1732 msg_puts((char *)fp->uf_name_exp); |
1741 msg_puts_attr("<SNR>", HL_ATTR(HLF_8)); | |
1742 msg_puts((char *)fp->uf_name + 3); | |
1743 } | |
1744 else | 1733 else |
1745 msg_puts((char *)fp->uf_name); | 1734 msg_puts((char *)fp->uf_name); |
1746 msg_putchar('('); | 1735 msg_putchar('('); |
1747 for (j = 0; j < fp->uf_args.ga_len; ++j) | 1736 for (j = 0; j < fp->uf_args.ga_len; ++j) |
1748 { | 1737 { |
2306 msg_putchar('\n'); // don't overwrite the function name | 2295 msg_putchar('\n'); // don't overwrite the function name |
2307 cmdline_row = msg_row; | 2296 cmdline_row = msg_row; |
2308 } | 2297 } |
2309 | 2298 |
2310 // Save the starting line number. | 2299 // Save the starting line number. |
2311 sourcing_lnum_top = sourcing_lnum; | 2300 sourcing_lnum_top = SOURCING_LNUM; |
2312 | 2301 |
2313 indent = 2; | 2302 indent = 2; |
2314 nesting = 0; | 2303 nesting = 0; |
2315 for (;;) | 2304 for (;;) |
2316 { | 2305 { |
2349 { | 2338 { |
2350 emsg(_("E126: Missing :endfunction")); | 2339 emsg(_("E126: Missing :endfunction")); |
2351 goto erret; | 2340 goto erret; |
2352 } | 2341 } |
2353 | 2342 |
2354 // Detect line continuation: sourcing_lnum increased more than one. | 2343 // Detect line continuation: SOURCING_LNUM increased more than one. |
2355 sourcing_lnum_off = get_sourced_lnum(eap->getline, eap->cookie); | 2344 sourcing_lnum_off = get_sourced_lnum(eap->getline, eap->cookie); |
2356 if (sourcing_lnum < sourcing_lnum_off) | 2345 if (SOURCING_LNUM < sourcing_lnum_off) |
2357 sourcing_lnum_off -= sourcing_lnum; | 2346 sourcing_lnum_off -= SOURCING_LNUM; |
2358 else | 2347 else |
2359 sourcing_lnum_off = 0; | 2348 sourcing_lnum_off = 0; |
2360 | 2349 |
2361 if (skip_until != NULL) | 2350 if (skip_until != NULL) |
2362 { | 2351 { |
2629 int slen, plen; | 2618 int slen, plen; |
2630 char_u *scriptname; | 2619 char_u *scriptname; |
2631 | 2620 |
2632 // Check that the autoload name matches the script name. | 2621 // Check that the autoload name matches the script name. |
2633 j = FAIL; | 2622 j = FAIL; |
2634 if (sourcing_name != NULL) | 2623 if (SOURCING_NAME != NULL) |
2635 { | 2624 { |
2636 scriptname = autoload_name(name); | 2625 scriptname = autoload_name(name); |
2637 if (scriptname != NULL) | 2626 if (scriptname != NULL) |
2638 { | 2627 { |
2639 p = vim_strchr(scriptname, '/'); | 2628 p = vim_strchr(scriptname, '/'); |
2640 plen = (int)STRLEN(p); | 2629 plen = (int)STRLEN(p); |
2641 slen = (int)STRLEN(sourcing_name); | 2630 slen = (int)STRLEN(SOURCING_NAME); |
2642 if (slen > plen && fnamecmp(p, | 2631 if (slen > plen && fnamecmp(p, |
2643 sourcing_name + slen - plen) == 0) | 2632 SOURCING_NAME + slen - plen) == 0) |
2644 j = OK; | 2633 j = OK; |
2645 vim_free(scriptname); | 2634 vim_free(scriptname); |
2646 } | 2635 } |
2647 } | 2636 } |
2648 if (j == FAIL) | 2637 if (j == FAIL) |
2683 // behave like "dict" was used | 2672 // behave like "dict" was used |
2684 flags |= FC_DICT; | 2673 flags |= FC_DICT; |
2685 } | 2674 } |
2686 | 2675 |
2687 // insert the new function in the function list | 2676 // insert the new function in the function list |
2688 STRCPY(fp->uf_name, name); | 2677 set_ufunc_name(fp, name); |
2689 if (overwrite) | 2678 if (overwrite) |
2690 { | 2679 { |
2691 hi = hash_find(&func_hashtab, name); | 2680 hi = hash_find(&func_hashtab, name); |
2692 hi->hi_key = UF2HIKEY(fp); | 2681 hi->hi_key = UF2HIKEY(fp); |
2693 } | 2682 } |
3351 | 3340 |
3352 // If breakpoints have been added/deleted need to check for it. | 3341 // If breakpoints have been added/deleted need to check for it. |
3353 if (fcp->dbg_tick != debug_tick) | 3342 if (fcp->dbg_tick != debug_tick) |
3354 { | 3343 { |
3355 fcp->breakpoint = dbg_find_breakpoint(FALSE, fp->uf_name, | 3344 fcp->breakpoint = dbg_find_breakpoint(FALSE, fp->uf_name, |
3356 sourcing_lnum); | 3345 SOURCING_LNUM); |
3357 fcp->dbg_tick = debug_tick; | 3346 fcp->dbg_tick = debug_tick; |
3358 } | 3347 } |
3359 #ifdef FEAT_PROFILE | 3348 #ifdef FEAT_PROFILE |
3360 if (do_profiling == PROF_YES) | 3349 if (do_profiling == PROF_YES) |
3361 func_line_end(cookie); | 3350 func_line_end(cookie); |
3374 if (fcp->linenr >= gap->ga_len) | 3363 if (fcp->linenr >= gap->ga_len) |
3375 retval = NULL; | 3364 retval = NULL; |
3376 else | 3365 else |
3377 { | 3366 { |
3378 retval = vim_strsave(((char_u **)(gap->ga_data))[fcp->linenr++]); | 3367 retval = vim_strsave(((char_u **)(gap->ga_data))[fcp->linenr++]); |
3379 sourcing_lnum = fcp->linenr; | 3368 SOURCING_LNUM = fcp->linenr; |
3380 #ifdef FEAT_PROFILE | 3369 #ifdef FEAT_PROFILE |
3381 if (do_profiling == PROF_YES) | 3370 if (do_profiling == PROF_YES) |
3382 func_line_start(cookie); | 3371 func_line_start(cookie); |
3383 #endif | 3372 #endif |
3384 } | 3373 } |
3385 } | 3374 } |
3386 | 3375 |
3387 // Did we encounter a breakpoint? | 3376 // Did we encounter a breakpoint? |
3388 if (fcp->breakpoint != 0 && fcp->breakpoint <= sourcing_lnum) | 3377 if (fcp->breakpoint != 0 && fcp->breakpoint <= SOURCING_LNUM) |
3389 { | 3378 { |
3390 dbg_breakpoint(fp->uf_name, sourcing_lnum); | 3379 dbg_breakpoint(fp->uf_name, SOURCING_LNUM); |
3391 // Find next breakpoint. | 3380 // Find next breakpoint. |
3392 fcp->breakpoint = dbg_find_breakpoint(FALSE, fp->uf_name, | 3381 fcp->breakpoint = dbg_find_breakpoint(FALSE, fp->uf_name, |
3393 sourcing_lnum); | 3382 SOURCING_LNUM); |
3394 fcp->dbg_tick = debug_tick; | 3383 fcp->dbg_tick = debug_tick; |
3395 } | 3384 } |
3396 | 3385 |
3397 return retval; | 3386 return retval; |
3398 } | 3387 } |