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;