diff 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
line wrap: on
line diff
--- a/src/scriptfile.c
+++ b/src/scriptfile.c
@@ -19,6 +19,122 @@ static garray_T		ga_loaded = {0, 0, size
 #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 @@ do_source(
     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 @@ do_source(
 	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 @@ do_source(
     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 @@ do_source(
 #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 @@ do_source(
 
     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 @@ get_sourced_lnum(char_u *(*fgetline)(int
 {
     return fgetline == getsourceline
 			? ((struct source_cookie *)cookie)->sourcing_lnum
-			: sourcing_lnum;
+			: SOURCING_LNUM;
 }
 
     static char_u *
@@ -1507,7 +1616,7 @@ getsourceline(int c UNUSED, void *cookie
     // 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 @@ getsourceline(int c UNUSED, void *cookie
 #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 @@ getsourceline(int c UNUSED, void *cookie
 
 #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