Mercurial > vim
comparison src/scriptfile.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 | b5b2e3b824c2 |
children | dcbc559510f6 |
comparison
equal
deleted
inserted
replaced
18990:1219ae40b086 | 18991:847cc7932c42 |
---|---|
15 | 15 |
16 #if defined(FEAT_EVAL) || defined(PROTO) | 16 #if defined(FEAT_EVAL) || defined(PROTO) |
17 // The names of packages that once were loaded are remembered. | 17 // The names of packages that once were loaded are remembered. |
18 static garray_T ga_loaded = {0, 0, sizeof(char_u *), 4, NULL}; | 18 static garray_T ga_loaded = {0, 0, sizeof(char_u *), 4, NULL}; |
19 #endif | 19 #endif |
20 | |
21 /* | |
22 * Initialize the execution stack. | |
23 */ | |
24 void | |
25 estack_init(void) | |
26 { | |
27 estack_T *entry; | |
28 | |
29 if (ga_grow(&exestack, 10) == FAIL) | |
30 mch_exit(0); | |
31 entry = ((estack_T *)exestack.ga_data) + exestack.ga_len; | |
32 entry->es_type = ETYPE_TOP; | |
33 entry->es_name = NULL; | |
34 entry->es_lnum = 0; | |
35 entry->es_info.ufunc = NULL; | |
36 ++exestack.ga_len; | |
37 } | |
38 | |
39 /* | |
40 * Add an item to the execution stack. | |
41 * Returns the new entry or NULL when out of memory. | |
42 */ | |
43 estack_T * | |
44 estack_push(etype_T type, char_u *name, long lnum) | |
45 { | |
46 estack_T *entry; | |
47 | |
48 // If memory allocation fails then we'll pop more than we push, eventually | |
49 // at the top level it will be OK again. | |
50 if (ga_grow(&exestack, 1) == OK) | |
51 { | |
52 entry = ((estack_T *)exestack.ga_data) + exestack.ga_len; | |
53 entry->es_type = type; | |
54 entry->es_name = name; | |
55 entry->es_lnum = lnum; | |
56 entry->es_info.ufunc = NULL; | |
57 ++exestack.ga_len; | |
58 return entry; | |
59 } | |
60 return NULL; | |
61 } | |
62 | |
63 /* | |
64 * Add a user function to the execution stack. | |
65 */ | |
66 void | |
67 estack_push_ufunc(etype_T type, ufunc_T *ufunc, long lnum) | |
68 { | |
69 estack_T *entry = estack_push(type, | |
70 ufunc->uf_name_exp != NULL | |
71 ? ufunc->uf_name_exp : ufunc->uf_name, lnum); | |
72 if (entry != NULL) | |
73 entry->es_info.ufunc = ufunc; | |
74 } | |
75 | |
76 /* | |
77 * Take an item off of the execution stack. | |
78 */ | |
79 void | |
80 estack_pop(void) | |
81 { | |
82 if (exestack.ga_len > 1) | |
83 --exestack.ga_len; | |
84 } | |
85 | |
86 /* | |
87 * Get the current value for <sfile> in allocated memory. | |
88 */ | |
89 char_u * | |
90 estack_sfile(void) | |
91 { | |
92 int len; | |
93 int idx; | |
94 estack_T *entry; | |
95 char *res; | |
96 int done; | |
97 | |
98 entry = ((estack_T *)exestack.ga_data) + exestack.ga_len - 1; | |
99 if (entry->es_name == NULL) | |
100 return NULL; | |
101 if (entry->es_info.ufunc == NULL) | |
102 return vim_strsave(entry->es_name); | |
103 | |
104 // For a function we compose the call stack, as it was done in the past: | |
105 // "function One[123]..Two[456]..Three" | |
106 len = STRLEN(entry->es_name) + 10; | |
107 for (idx = exestack.ga_len - 2; idx >= 0; --idx) | |
108 { | |
109 entry = ((estack_T *)exestack.ga_data) + idx; | |
110 if (entry->es_name == NULL || entry->es_info.ufunc == NULL) | |
111 { | |
112 ++idx; | |
113 break; | |
114 } | |
115 len += STRLEN(entry->es_name) + 15; | |
116 } | |
117 | |
118 res = (char *)alloc(len); | |
119 if (res != NULL) | |
120 { | |
121 STRCPY(res, "function "); | |
122 while (idx < exestack.ga_len - 1) | |
123 { | |
124 done = STRLEN(res); | |
125 entry = ((estack_T *)exestack.ga_data) + idx; | |
126 vim_snprintf(res + done, len - done, "%s[%ld]..", | |
127 entry->es_name, entry->es_lnum); | |
128 ++idx; | |
129 } | |
130 done = STRLEN(res); | |
131 entry = ((estack_T *)exestack.ga_data) + idx; | |
132 vim_snprintf(res + done, len - done, "%s", entry->es_name); | |
133 } | |
134 return (char_u *)res; | |
135 } | |
20 | 136 |
21 /* | 137 /* |
22 * ":runtime [what] {name}" | 138 * ":runtime [what] {name}" |
23 */ | 139 */ |
24 void | 140 void |
945 char_u *fname, | 1061 char_u *fname, |
946 int check_other, // check for .vimrc and _vimrc | 1062 int check_other, // check for .vimrc and _vimrc |
947 int is_vimrc) // DOSO_ value | 1063 int is_vimrc) // DOSO_ value |
948 { | 1064 { |
949 struct source_cookie cookie; | 1065 struct source_cookie cookie; |
950 char_u *save_sourcing_name; | |
951 linenr_T save_sourcing_lnum; | |
952 char_u *p; | 1066 char_u *p; |
953 char_u *fname_exp; | 1067 char_u *fname_exp; |
954 char_u *firstline = NULL; | 1068 char_u *firstline = NULL; |
955 int retval = FAIL; | 1069 int retval = FAIL; |
956 #ifdef FEAT_EVAL | 1070 #ifdef FEAT_EVAL |
1037 if (cookie.fp == NULL) | 1151 if (cookie.fp == NULL) |
1038 { | 1152 { |
1039 if (p_verbose > 0) | 1153 if (p_verbose > 0) |
1040 { | 1154 { |
1041 verbose_enter(); | 1155 verbose_enter(); |
1042 if (sourcing_name == NULL) | 1156 if (SOURCING_NAME == NULL) |
1043 smsg(_("could not source \"%s\""), fname); | 1157 smsg(_("could not source \"%s\""), fname); |
1044 else | 1158 else |
1045 smsg(_("line %ld: could not source \"%s\""), | 1159 smsg(_("line %ld: could not source \"%s\""), |
1046 sourcing_lnum, fname); | 1160 SOURCING_LNUM, fname); |
1047 verbose_leave(); | 1161 verbose_leave(); |
1048 } | 1162 } |
1049 goto theend; | 1163 goto theend; |
1050 } | 1164 } |
1051 | 1165 |
1053 // - In verbose mode, give a message. | 1167 // - In verbose mode, give a message. |
1054 // - For a vimrc file, may want to set 'compatible', call vimrc_found(). | 1168 // - For a vimrc file, may want to set 'compatible', call vimrc_found(). |
1055 if (p_verbose > 1) | 1169 if (p_verbose > 1) |
1056 { | 1170 { |
1057 verbose_enter(); | 1171 verbose_enter(); |
1058 if (sourcing_name == NULL) | 1172 if (SOURCING_NAME == NULL) |
1059 smsg(_("sourcing \"%s\""), fname); | 1173 smsg(_("sourcing \"%s\""), fname); |
1060 else | 1174 else |
1061 smsg(_("line %ld: sourcing \"%s\""), | 1175 smsg(_("line %ld: sourcing \"%s\""), SOURCING_LNUM, fname); |
1062 sourcing_lnum, fname); | |
1063 verbose_leave(); | 1176 verbose_leave(); |
1064 } | 1177 } |
1065 if (is_vimrc == DOSO_VIMRC) | 1178 if (is_vimrc == DOSO_VIMRC) |
1066 vimrc_found(fname_exp, (char_u *)"MYVIMRC"); | 1179 vimrc_found(fname_exp, (char_u *)"MYVIMRC"); |
1067 else if (is_vimrc == DOSO_GVIMRC) | 1180 else if (is_vimrc == DOSO_GVIMRC) |
1088 | 1201 |
1089 cookie.level = ex_nesting_level; | 1202 cookie.level = ex_nesting_level; |
1090 #endif | 1203 #endif |
1091 | 1204 |
1092 // Keep the sourcing name/lnum, for recursive calls. | 1205 // Keep the sourcing name/lnum, for recursive calls. |
1093 save_sourcing_name = sourcing_name; | 1206 estack_push(ETYPE_SCRIPT, fname_exp, 0); |
1094 sourcing_name = fname_exp; | |
1095 save_sourcing_lnum = sourcing_lnum; | |
1096 sourcing_lnum = 0; | |
1097 | 1207 |
1098 #ifdef STARTUPTIME | 1208 #ifdef STARTUPTIME |
1099 if (time_fd != NULL) | 1209 if (time_fd != NULL) |
1100 time_push(&tv_rel, &tv_start); | 1210 time_push(&tv_rel, &tv_start); |
1101 #endif | 1211 #endif |
1231 } | 1341 } |
1232 #endif | 1342 #endif |
1233 | 1343 |
1234 if (got_int) | 1344 if (got_int) |
1235 emsg(_(e_interr)); | 1345 emsg(_(e_interr)); |
1236 sourcing_name = save_sourcing_name; | 1346 estack_pop(); |
1237 sourcing_lnum = save_sourcing_lnum; | |
1238 if (p_verbose > 1) | 1347 if (p_verbose > 1) |
1239 { | 1348 { |
1240 verbose_enter(); | 1349 verbose_enter(); |
1241 smsg(_("finished sourcing %s"), fname); | 1350 smsg(_("finished sourcing %s"), fname); |
1242 if (sourcing_name != NULL) | 1351 if (SOURCING_NAME != NULL) |
1243 smsg(_("continuing in %s"), sourcing_name); | 1352 smsg(_("continuing in %s"), SOURCING_NAME); |
1244 verbose_leave(); | 1353 verbose_leave(); |
1245 } | 1354 } |
1246 #ifdef STARTUPTIME | 1355 #ifdef STARTUPTIME |
1247 if (time_fd != NULL) | 1356 if (time_fd != NULL) |
1248 { | 1357 { |
1379 linenr_T | 1488 linenr_T |
1380 get_sourced_lnum(char_u *(*fgetline)(int, void *, int, int), void *cookie) | 1489 get_sourced_lnum(char_u *(*fgetline)(int, void *, int, int), void *cookie) |
1381 { | 1490 { |
1382 return fgetline == getsourceline | 1491 return fgetline == getsourceline |
1383 ? ((struct source_cookie *)cookie)->sourcing_lnum | 1492 ? ((struct source_cookie *)cookie)->sourcing_lnum |
1384 : sourcing_lnum; | 1493 : SOURCING_LNUM; |
1385 } | 1494 } |
1386 | 1495 |
1387 static char_u * | 1496 static char_u * |
1388 get_one_sourceline(struct source_cookie *sp) | 1497 get_one_sourceline(struct source_cookie *sp) |
1389 { | 1498 { |
1505 | 1614 |
1506 #ifdef FEAT_EVAL | 1615 #ifdef FEAT_EVAL |
1507 // If breakpoints have been added/deleted need to check for it. | 1616 // If breakpoints have been added/deleted need to check for it. |
1508 if (sp->dbg_tick < debug_tick) | 1617 if (sp->dbg_tick < debug_tick) |
1509 { | 1618 { |
1510 sp->breakpoint = dbg_find_breakpoint(TRUE, sp->fname, sourcing_lnum); | 1619 sp->breakpoint = dbg_find_breakpoint(TRUE, sp->fname, SOURCING_LNUM); |
1511 sp->dbg_tick = debug_tick; | 1620 sp->dbg_tick = debug_tick; |
1512 } | 1621 } |
1513 # ifdef FEAT_PROFILE | 1622 # ifdef FEAT_PROFILE |
1514 if (do_profiling == PROF_YES) | 1623 if (do_profiling == PROF_YES) |
1515 script_line_end(); | 1624 script_line_end(); |
1516 # endif | 1625 # endif |
1517 #endif | 1626 #endif |
1518 | 1627 |
1519 // Set the current sourcing line number. | 1628 // Set the current sourcing line number. |
1520 sourcing_lnum = sp->sourcing_lnum + 1; | 1629 SOURCING_LNUM = sp->sourcing_lnum + 1; |
1521 | 1630 |
1522 // Get current line. If there is a read-ahead line, use it, otherwise get | 1631 // Get current line. If there is a read-ahead line, use it, otherwise get |
1523 // one now. | 1632 // one now. |
1524 if (sp->finished) | 1633 if (sp->finished) |
1525 line = NULL; | 1634 line = NULL; |
1600 } | 1709 } |
1601 } | 1710 } |
1602 | 1711 |
1603 #ifdef FEAT_EVAL | 1712 #ifdef FEAT_EVAL |
1604 // Did we encounter a breakpoint? | 1713 // Did we encounter a breakpoint? |
1605 if (sp->breakpoint != 0 && sp->breakpoint <= sourcing_lnum) | 1714 if (sp->breakpoint != 0 && sp->breakpoint <= SOURCING_LNUM) |
1606 { | 1715 { |
1607 dbg_breakpoint(sp->fname, sourcing_lnum); | 1716 dbg_breakpoint(sp->fname, SOURCING_LNUM); |
1608 // Find next breakpoint. | 1717 // Find next breakpoint. |
1609 sp->breakpoint = dbg_find_breakpoint(TRUE, sp->fname, sourcing_lnum); | 1718 sp->breakpoint = dbg_find_breakpoint(TRUE, sp->fname, SOURCING_LNUM); |
1610 sp->dbg_tick = debug_tick; | 1719 sp->dbg_tick = debug_tick; |
1611 } | 1720 } |
1612 #endif | 1721 #endif |
1613 | 1722 |
1614 return line; | 1723 return line; |