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.
diff --git a/src/scriptfile.c b/src/scriptfile.c
index 740950f..d471839 100644
--- a/src/scriptfile.c
+++ b/src/scriptfile.c
@@ -19,6 +19,122 @@
#endif
/*
+ * Initialize the execution stack.
+ */
+ void
+estack_init(void)
+{
+ estack_T *entry;
+
+ if (ga_grow(&exestack, 10) == FAIL)
+ mch_exit(0);
+ entry = ((estack_T *)exestack.ga_data) + exestack.ga_len;
+ entry->es_type = ETYPE_TOP;
+ entry->es_name = NULL;
+ entry->es_lnum = 0;
+ entry->es_info.ufunc = NULL;
+ ++exestack.ga_len;
+}
+
+/*
+ * Add an item to the execution stack.
+ * Returns the new entry or NULL when out of memory.
+ */
+ estack_T *
+estack_push(etype_T type, char_u *name, long lnum)
+{
+ estack_T *entry;
+
+ // If memory allocation fails then we'll pop more than we push, eventually
+ // at the top level it will be OK again.
+ if (ga_grow(&exestack, 1) == OK)
+ {
+ entry = ((estack_T *)exestack.ga_data) + exestack.ga_len;
+ entry->es_type = type;
+ entry->es_name = name;
+ entry->es_lnum = lnum;
+ entry->es_info.ufunc = NULL;
+ ++exestack.ga_len;
+ return entry;
+ }
+ return NULL;
+}
+
+/*
+ * Add a user function to the execution stack.
+ */
+ void
+estack_push_ufunc(etype_T type, ufunc_T *ufunc, long lnum)
+{
+ estack_T *entry = estack_push(type,
+ ufunc->uf_name_exp != NULL
+ ? ufunc->uf_name_exp : ufunc->uf_name, lnum);
+ if (entry != NULL)
+ entry->es_info.ufunc = ufunc;
+}
+
+/*
+ * Take an item off of the execution stack.
+ */
+ void
+estack_pop(void)
+{
+ if (exestack.ga_len > 1)
+ --exestack.ga_len;
+}
+
+/*
+ * Get the current value for <sfile> in allocated memory.
+ */
+ char_u *
+estack_sfile(void)
+{
+ int len;
+ int idx;
+ estack_T *entry;
+ char *res;
+ int done;
+
+ entry = ((estack_T *)exestack.ga_data) + exestack.ga_len - 1;
+ if (entry->es_name == NULL)
+ return NULL;
+ if (entry->es_info.ufunc == NULL)
+ return vim_strsave(entry->es_name);
+
+ // For a function we compose the call stack, as it was done in the past:
+ // "function One[123]..Two[456]..Three"
+ len = STRLEN(entry->es_name) + 10;
+ for (idx = exestack.ga_len - 2; idx >= 0; --idx)
+ {
+ entry = ((estack_T *)exestack.ga_data) + idx;
+ if (entry->es_name == NULL || entry->es_info.ufunc == NULL)
+ {
+ ++idx;
+ break;
+ }
+ len += STRLEN(entry->es_name) + 15;
+ }
+
+ res = (char *)alloc(len);
+ if (res != NULL)
+ {
+ STRCPY(res, "function ");
+ while (idx < exestack.ga_len - 1)
+ {
+ done = STRLEN(res);
+ entry = ((estack_T *)exestack.ga_data) + idx;
+ vim_snprintf(res + done, len - done, "%s[%ld]..",
+ entry->es_name, entry->es_lnum);
+ ++idx;
+ }
+ done = STRLEN(res);
+ entry = ((estack_T *)exestack.ga_data) + idx;
+ vim_snprintf(res + done, len - done, "%s", entry->es_name);
+ }
+ return (char_u *)res;
+}
+
+/*
* ":runtime [what] {name}"
*/
void
@@ -947,8 +1063,6 @@
int is_vimrc) // DOSO_ value
{
struct source_cookie cookie;
- char_u *save_sourcing_name;
- linenr_T save_sourcing_lnum;
char_u *p;
char_u *fname_exp;
char_u *firstline = NULL;
@@ -1039,11 +1153,11 @@
if (p_verbose > 0)
{
verbose_enter();
- if (sourcing_name == NULL)
+ if (SOURCING_NAME == NULL)
smsg(_("could not source \"%s\""), fname);
else
smsg(_("line %ld: could not source \"%s\""),
- sourcing_lnum, fname);
+ SOURCING_LNUM, fname);
verbose_leave();
}
goto theend;
@@ -1055,11 +1169,10 @@
if (p_verbose > 1)
{
verbose_enter();
- if (sourcing_name == NULL)
+ if (SOURCING_NAME == NULL)
smsg(_("sourcing \"%s\""), fname);
else
- smsg(_("line %ld: sourcing \"%s\""),
- sourcing_lnum, fname);
+ smsg(_("line %ld: sourcing \"%s\""), SOURCING_LNUM, fname);
verbose_leave();
}
if (is_vimrc == DOSO_VIMRC)
@@ -1090,10 +1203,7 @@
#endif
// Keep the sourcing name/lnum, for recursive calls.
- save_sourcing_name = sourcing_name;
- sourcing_name = fname_exp;
- save_sourcing_lnum = sourcing_lnum;
- sourcing_lnum = 0;
+ estack_push(ETYPE_SCRIPT, fname_exp, 0);
#ifdef STARTUPTIME
if (time_fd != NULL)
@@ -1233,14 +1343,13 @@
if (got_int)
emsg(_(e_interr));
- sourcing_name = save_sourcing_name;
- sourcing_lnum = save_sourcing_lnum;
+ estack_pop();
if (p_verbose > 1)
{
verbose_enter();
smsg(_("finished sourcing %s"), fname);
- if (sourcing_name != NULL)
- smsg(_("continuing in %s"), sourcing_name);
+ if (SOURCING_NAME != NULL)
+ smsg(_("continuing in %s"), SOURCING_NAME);
verbose_leave();
}
#ifdef STARTUPTIME
@@ -1381,7 +1490,7 @@
{
return fgetline == getsourceline
? ((struct source_cookie *)cookie)->sourcing_lnum
- : sourcing_lnum;
+ : SOURCING_LNUM;
}
static char_u *
@@ -1507,7 +1616,7 @@
// If breakpoints have been added/deleted need to check for it.
if (sp->dbg_tick < debug_tick)
{
- sp->breakpoint = dbg_find_breakpoint(TRUE, sp->fname, sourcing_lnum);
+ sp->breakpoint = dbg_find_breakpoint(TRUE, sp->fname, SOURCING_LNUM);
sp->dbg_tick = debug_tick;
}
# ifdef FEAT_PROFILE
@@ -1517,7 +1626,7 @@
#endif
// Set the current sourcing line number.
- sourcing_lnum = sp->sourcing_lnum + 1;
+ SOURCING_LNUM = sp->sourcing_lnum + 1;
// Get current line. If there is a read-ahead line, use it, otherwise get
// one now.
@@ -1602,11 +1711,11 @@
#ifdef FEAT_EVAL
// Did we encounter a breakpoint?
- if (sp->breakpoint != 0 && sp->breakpoint <= sourcing_lnum)
+ if (sp->breakpoint != 0 && sp->breakpoint <= SOURCING_LNUM)
{
- dbg_breakpoint(sp->fname, sourcing_lnum);
+ dbg_breakpoint(sp->fname, SOURCING_LNUM);
// Find next breakpoint.
- sp->breakpoint = dbg_find_breakpoint(TRUE, sp->fname, sourcing_lnum);
+ sp->breakpoint = dbg_find_breakpoint(TRUE, sp->fname, SOURCING_LNUM);
sp->dbg_tick = debug_tick;
}
#endif