changeset 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 1219ae40b086
children 31a1be1dd3ea
files src/autocmd.c src/buffer.c src/debugger.c src/ex_docmd.c src/ex_eval.c src/globals.h src/highlight.c src/kword_test.c src/main.c src/map.c src/message.c src/option.c src/profiler.c src/proto/scriptfile.pro src/scriptfile.c src/spellfile.c src/structs.h src/term.c src/testdir/test_debugger.vim src/testing.c src/usercmd.c src/userfunc.c src/version.c
diffstat 23 files changed, 387 insertions(+), 242 deletions(-) [+]
line wrap: on
line diff
--- a/src/autocmd.c
+++ b/src/autocmd.c
@@ -218,7 +218,7 @@ static AutoPat *last_autopat[NUM_EVENTS]
 /*
  * struct used to keep status while executing autocommands for an event.
  */
-typedef struct AutoPatCmd
+struct AutoPatCmd_S
 {
     AutoPat	*curpat;	// next AutoPat to examine
     AutoCmd	*nextcmd;	// next AutoCmd to execute
@@ -229,8 +229,8 @@ typedef struct AutoPatCmd
     event_T	event;		// current event
     int		arg_bufnr;	// Initially equal to <abuf>, set to zero when
 				// buf is deleted.
-    struct AutoPatCmd   *next;	// chain of active apc-s for auto-invalidation
-} AutoPatCmd;
+    AutoPatCmd   *next;		// chain of active apc-s for auto-invalidation
+};
 
 static AutoPatCmd *active_apc_list = NULL; // stack of active autocommands
 
@@ -1242,7 +1242,7 @@ do_autocmd_event(
 	    ac->cmd = vim_strsave(cmd);
 #ifdef FEAT_EVAL
 	    ac->script_ctx = current_sctx;
-	    ac->script_ctx.sc_lnum += sourcing_lnum;
+	    ac->script_ctx.sc_lnum += SOURCING_LNUM;
 #endif
 	    if (ac->cmd == NULL)
 	    {
@@ -1805,8 +1805,6 @@ apply_autocmds_group(
     int		save_changed;
     buf_T	*old_curbuf;
     int		retval = FALSE;
-    char_u	*save_sourcing_name;
-    linenr_T	save_sourcing_lnum;
     char_u	*save_autocmd_fname;
     int		save_autocmd_fname_full;
     int		save_autocmd_bufnr;
@@ -2020,10 +2018,9 @@ apply_autocmds_group(
 
     // Don't redraw while doing autocommands.
     ++RedrawingDisabled;
-    save_sourcing_name = sourcing_name;
-    sourcing_name = NULL;	// don't free this one
-    save_sourcing_lnum = sourcing_lnum;
-    sourcing_lnum = 0;		// no line number here
+
+    // name and lnum are filled in later
+    estack_push(ETYPE_AUCMD, NULL, 0);
 
 #ifdef FEAT_EVAL
     save_current_sctx = current_sctx;
@@ -2126,9 +2123,8 @@ apply_autocmds_group(
     autocmd_busy = save_autocmd_busy;
     filechangeshell_busy = FALSE;
     autocmd_nested = save_autocmd_nested;
-    vim_free(sourcing_name);
-    sourcing_name = save_sourcing_name;
-    sourcing_lnum = save_sourcing_lnum;
+    vim_free(SOURCING_NAME);
+    estack_pop();
     vim_free(autocmd_fname);
     autocmd_fname = save_autocmd_fname;
     autocmd_fname_full = save_autocmd_fname_full;
@@ -2256,8 +2252,9 @@ auto_next_pat(
     AutoCmd	*cp;
     char_u	*name;
     char	*s;
+    char_u	**sourcing_namep = &SOURCING_NAME;
 
-    VIM_CLEAR(sourcing_name);
+    VIM_CLEAR(*sourcing_namep);
 
     for (ap = apc->curpat; ap != NULL && !got_int; ap = ap->next)
     {
@@ -2277,16 +2274,16 @@ auto_next_pat(
 	    {
 		name = event_nr2name(apc->event);
 		s = _("%s Autocommands for \"%s\"");
-		sourcing_name = alloc(STRLEN(s)
+		*sourcing_namep = alloc(STRLEN(s)
 					      + STRLEN(name) + ap->patlen + 1);
-		if (sourcing_name != NULL)
+		if (*sourcing_namep != NULL)
 		{
-		    sprintf((char *)sourcing_name, s,
+		    sprintf((char *)*sourcing_namep, s,
 					       (char *)name, (char *)ap->pat);
 		    if (p_verbose >= 8)
 		    {
 			verbose_enter();
-			smsg(_("Executing %s"), sourcing_name);
+			smsg(_("Executing %s"), *sourcing_namep);
 			verbose_leave();
 		    }
 		}
--- a/src/buffer.c
+++ b/src/buffer.c
@@ -5279,8 +5279,6 @@ chk_modeline(
     int		vers;
     int		end;
     int		retval = OK;
-    char_u	*save_sourcing_name;
-    linenr_T	save_sourcing_lnum;
 #ifdef FEAT_EVAL
     sctx_T	save_current_sctx;
 #endif
@@ -5325,10 +5323,8 @@ chk_modeline(
 	if (linecopy == NULL)
 	    return FAIL;
 
-	save_sourcing_lnum = sourcing_lnum;
-	save_sourcing_name = sourcing_name;
-	sourcing_lnum = lnum;		// prepare for emsg()
-	sourcing_name = (char_u *)"modelines";
+	// prepare for emsg()
+	estack_push(ETYPE_MODELINE, (char_u *)"modelines", lnum);
 
 	end = FALSE;
 	while (end == FALSE)
@@ -5371,7 +5367,7 @@ chk_modeline(
 		save_current_sctx = current_sctx;
 		current_sctx.sc_sid = SID_MODELINE;
 		current_sctx.sc_seq = 0;
-		current_sctx.sc_lnum = 0;
+		current_sctx.sc_lnum = lnum;
 		current_sctx.sc_version = 1;
 #endif
 		// Make sure no risky things are executed as a side effect.
@@ -5389,9 +5385,7 @@ chk_modeline(
 	    s = e + 1;			// advance to next part
 	}
 
-	sourcing_lnum = save_sourcing_lnum;
-	sourcing_name = save_sourcing_name;
-
+	estack_pop();
 	vim_free(linecopy);
     }
     return retval;
--- a/src/debugger.c
+++ b/src/debugger.c
@@ -51,6 +51,7 @@ do_debug(char_u *cmd)
     int		n;
     char_u	*cmdline = NULL;
     char_u	*p;
+    char_u	*sname;
     char	*tail = NULL;
     static int	last_cmd = 0;
 #define CMD_CONT	1
@@ -104,10 +105,12 @@ do_debug(char_u *cmd)
 	vim_free(debug_newval);
 	debug_newval = NULL;
     }
-    if (sourcing_name != NULL)
-	msg((char *)sourcing_name);
-    if (sourcing_lnum != 0)
-	smsg(_("line %ld: %s"), (long)sourcing_lnum, cmd);
+    sname = estack_sfile();
+    if (sname != NULL)
+	msg((char *)sname);
+    vim_free(sname);
+    if (SOURCING_LNUM != 0)
+	smsg(_("line %ld: %s"), SOURCING_LNUM, cmd);
     else
 	smsg(_("cmd: %s"), cmd);
 
@@ -300,14 +303,14 @@ do_debug(char_u *cmd)
 }
 
     static int
-get_maxbacktrace_level(void)
+get_maxbacktrace_level(char_u *sname)
 {
     char	*p, *q;
     int		maxbacktrace = 0;
 
-    if (sourcing_name != NULL)
+    if (sname != NULL)
     {
-	p = (char *)sourcing_name;
+	p = (char *)sname;
 	while ((q = strstr(p, "..")) != NULL)
 	{
 	    p = q + 2;
@@ -341,27 +344,32 @@ do_checkbacktracelevel(void)
     }
     else
     {
-	int max = get_maxbacktrace_level();
+	char_u	*sname = estack_sfile();
+	int	max = get_maxbacktrace_level(sname);
 
 	if (debug_backtrace_level > max)
 	{
 	    debug_backtrace_level = max;
 	    smsg(_("frame at highest level: %d"), max);
 	}
+	vim_free(sname);
     }
 }
 
     static void
 do_showbacktrace(char_u *cmd)
 {
+    char_u  *sname;
     char    *cur;
     char    *next;
     int	    i = 0;
-    int	    max = get_maxbacktrace_level();
+    int	    max;
 
-    if (sourcing_name != NULL)
+    sname = estack_sfile();
+    max = get_maxbacktrace_level(sname);
+    if (sname != NULL)
     {
-	cur = (char *)sourcing_name;
+	cur = (char *)sname;
 	while (!got_int)
 	{
 	    next = strstr(cur, "..");
@@ -377,9 +385,11 @@ do_showbacktrace(char_u *cmd)
 	    *next = '.';
 	    cur = next + 2;
 	}
+	vim_free(sname);
     }
-    if (sourcing_lnum != 0)
-       smsg(_("line %ld: %s"), (long)sourcing_lnum, cmd);
+
+    if (SOURCING_LNUM != 0)
+       smsg(_("line %ld: %s"), (long)SOURCING_LNUM, cmd);
     else
        smsg(_("cmd: %s"), cmd);
 }
--- a/src/ex_docmd.c
+++ b/src/ex_docmd.c
@@ -703,7 +703,7 @@ do_cmdline(
     }
     else if (getline_equal(fgetline, cookie, getsourceline))
     {
-	fname = sourcing_name;
+	fname = SOURCING_NAME;
 	breakpoint = source_breakpoint(real_cookie);
 	dbg_tick = source_dbg_tick(real_cookie);
     }
@@ -819,22 +819,22 @@ do_cmdline(
 	    {
 		*breakpoint = dbg_find_breakpoint(
 				getline_equal(fgetline, cookie, getsourceline),
-							fname, sourcing_lnum);
+							fname, SOURCING_LNUM);
 		*dbg_tick = debug_tick;
 	    }
 
 	    next_cmdline = ((wcmd_T *)(lines_ga.ga_data))[current_line].line;
-	    sourcing_lnum = ((wcmd_T *)(lines_ga.ga_data))[current_line].lnum;
+	    SOURCING_LNUM = ((wcmd_T *)(lines_ga.ga_data))[current_line].lnum;
 
 	    // Did we encounter a breakpoint?
 	    if (breakpoint != NULL && *breakpoint != 0
-					      && *breakpoint <= sourcing_lnum)
+					      && *breakpoint <= SOURCING_LNUM)
 	    {
-		dbg_breakpoint(fname, sourcing_lnum);
+		dbg_breakpoint(fname, SOURCING_LNUM);
 		// Find next breakpoint.
 		*breakpoint = dbg_find_breakpoint(
 			       getline_equal(fgetline, cookie, getsourceline),
-							fname, sourcing_lnum);
+							fname, SOURCING_LNUM);
 		*dbg_tick = debug_tick;
 	    }
 # ifdef FEAT_PROFILE
@@ -963,8 +963,8 @@ do_cmdline(
 	    }
 	}
 
-	if (p_verbose >= 15 && sourcing_name != NULL)
-	    msg_verbose_cmd(sourcing_lnum, cmdline_copy);
+	if (p_verbose >= 15 && SOURCING_NAME != NULL)
+	    msg_verbose_cmd(SOURCING_LNUM, cmdline_copy);
 
 	/*
 	 * 2. Execute one '|' separated command.
@@ -1081,7 +1081,7 @@ do_cmdline(
 	// Check for the next breakpoint after a watchexpression
 	if (breakpoint != NULL && has_watchexpr())
 	{
-	    *breakpoint = dbg_find_breakpoint(FALSE, fname, sourcing_lnum);
+	    *breakpoint = dbg_find_breakpoint(FALSE, fname, SOURCING_LNUM);
 	    *dbg_tick = debug_tick;
 	}
 
@@ -1092,7 +1092,7 @@ do_cmdline(
 	{
 	    if (lines_ga.ga_len > 0)
 	    {
-		sourcing_lnum =
+		SOURCING_LNUM =
 		       ((wcmd_T *)lines_ga.ga_data)[lines_ga.ga_len - 1].lnum;
 		free_cmdlines(&lines_ga);
 	    }
@@ -1234,8 +1234,6 @@ do_cmdline(
 	if (did_throw)
 	{
 	    void	*p = NULL;
-	    char_u	*saved_sourcing_name;
-	    int		saved_sourcing_lnum;
 	    struct msglist	*messages = NULL, *next;
 
 	    /*
@@ -1260,10 +1258,8 @@ do_cmdline(
 		    break;
 	    }
 
-	    saved_sourcing_name = sourcing_name;
-	    saved_sourcing_lnum = sourcing_lnum;
-	    sourcing_name = current_exception->throw_name;
-	    sourcing_lnum = current_exception->throw_lnum;
+	    estack_push(ETYPE_EXCEPT, current_exception->throw_name,
+						current_exception->throw_lnum);
 	    current_exception->throw_name = NULL;
 
 	    discard_current_exception();	// uses IObuff if 'verbose'
@@ -1287,9 +1283,8 @@ do_cmdline(
 		emsg(p);
 		vim_free(p);
 	    }
-	    vim_free(sourcing_name);
-	    sourcing_name = saved_sourcing_name;
-	    sourcing_lnum = saved_sourcing_lnum;
+	    vim_free(SOURCING_NAME);
+	    estack_pop();
 	}
 
 	/*
@@ -1428,7 +1423,7 @@ get_loop_line(int c, void *cookie, int i
     KeyTyped = FALSE;
     ++cp->current_line;
     wp = (wcmd_T *)(cp->lines_gap->ga_data) + cp->current_line;
-    sourcing_lnum = wp->lnum;
+    SOURCING_LNUM = wp->lnum;
     return vim_strsave(wp->line);
 }
 
@@ -1441,7 +1436,7 @@ store_loop_line(garray_T *gap, char_u *l
     if (ga_grow(gap, 1) == FAIL)
 	return FAIL;
     ((wcmd_T *)(gap->ga_data))[gap->ga_len].line = vim_strsave(line);
-    ((wcmd_T *)(gap->ga_data))[gap->ga_len].lnum = sourcing_lnum;
+    ((wcmd_T *)(gap->ga_data))[gap->ga_len].lnum = SOURCING_LNUM;
     ++gap->ga_len;
     return OK;
 }
@@ -8171,33 +8166,34 @@ eval_vars(
 		break;
 
 	case SPEC_SFILE:	// file name for ":so" command
-		result = sourcing_name;
+		result = estack_sfile();
 		if (result == NULL)
 		{
 		    *errormsg = _("E498: no :source file name to substitute for \"<sfile>\"");
 		    return NULL;
 		}
+		resultbuf = result;	    // remember allocated string
 		break;
 
 	case SPEC_SLNUM:	// line in file for ":so" command
-		if (sourcing_name == NULL || sourcing_lnum == 0)
+		if (SOURCING_NAME == NULL || SOURCING_LNUM == 0)
 		{
 		    *errormsg = _("E842: no line number to use for \"<slnum>\"");
 		    return NULL;
 		}
-		sprintf((char *)strbuf, "%ld", (long)sourcing_lnum);
+		sprintf((char *)strbuf, "%ld", SOURCING_LNUM);
 		result = strbuf;
 		break;
 
 #ifdef FEAT_EVAL
 	case SPEC_SFLNUM:	// line in script file
-		if (current_sctx.sc_lnum + sourcing_lnum == 0)
+		if (current_sctx.sc_lnum + SOURCING_LNUM == 0)
 		{
 		    *errormsg = _("E961: no line number to use for \"<sflnum>\"");
 		    return NULL;
 		}
 		sprintf((char *)strbuf, "%ld",
-				 (long)(current_sctx.sc_lnum + sourcing_lnum));
+				 (long)(current_sctx.sc_lnum + SOURCING_LNUM));
 		result = strbuf;
 		break;
 #endif
--- a/src/ex_eval.c
+++ b/src/ex_eval.c
@@ -534,15 +534,16 @@ throw_exception(void *value, except_type
 	goto nomem;
 
     excp->type = type;
-    excp->throw_name = vim_strsave(sourcing_name == NULL
-					      ? (char_u *)"" : sourcing_name);
+    excp->throw_name = estack_sfile();
+    if (excp->throw_name == NULL)
+	excp->throw_name = vim_strsave((char_u *)"");
     if (excp->throw_name == NULL)
     {
 	if (should_free)
 	    vim_free(excp->value);
 	goto nomem;
     }
-    excp->throw_lnum = sourcing_lnum;
+    excp->throw_lnum = SOURCING_LNUM;
 
     if (p_verbose >= 13 || debug_break_level > 0)
     {
--- a/src/globals.h
+++ b/src/globals.h
@@ -266,8 +266,15 @@ EXTERN int	lines_left INIT(= -1);	    //
 EXTERN int	msg_no_more INIT(= FALSE);  // don't use more prompt, truncate
 					    // messages
 
-EXTERN char_u	*sourcing_name INIT( = NULL);// name of error message source
-EXTERN linenr_T	sourcing_lnum INIT(= 0);    // line number of the source file
+/*
+ * Stack of execution contexts.  Each entry is an estack_T.
+ * Current context is at ga_len - 1.
+ */
+EXTERN garray_T	exestack INIT(= {0 COMMA 0 COMMA sizeof(estack_T) COMMA 50 COMMA NULL});
+// name of error message source
+#define SOURCING_NAME (((estack_T *)exestack.ga_data)[exestack.ga_len - 1].es_name)
+// line number in the message source or zero
+#define SOURCING_LNUM (((estack_T *)exestack.ga_data)[exestack.ga_len - 1].es_lnum)
 
 #ifdef FEAT_EVAL
 EXTERN int	ex_nesting_level INIT(= 0);	// nesting level
--- a/src/highlight.c
+++ b/src/highlight.c
@@ -748,7 +748,7 @@ do_highlight(
 	    if (to_id > 0 && !forceit && !init
 				   && hl_has_settings(from_id - 1, dodefault))
 	    {
-		if (sourcing_name == NULL && !dodefault)
+		if (SOURCING_NAME == NULL && !dodefault)
 		    emsg(_("E414: group has settings, highlight link ignored"));
 	    }
 	    else if (HL_TABLE()[from_id - 1].sg_link != to_id
@@ -763,7 +763,7 @@ do_highlight(
 		HL_TABLE()[from_id - 1].sg_link = to_id;
 #ifdef FEAT_EVAL
 		HL_TABLE()[from_id - 1].sg_script_ctx = current_sctx;
-		HL_TABLE()[from_id - 1].sg_script_ctx.sc_lnum += sourcing_lnum;
+		HL_TABLE()[from_id - 1].sg_script_ctx.sc_lnum += SOURCING_LNUM;
 #endif
 		HL_TABLE()[from_id - 1].sg_cleared = FALSE;
 		redraw_all_later(SOME_VALID);
@@ -1518,7 +1518,7 @@ do_highlight(
 	    set_hl_attr(idx);
 #ifdef FEAT_EVAL
 	HL_TABLE()[idx].sg_script_ctx = current_sctx;
-	HL_TABLE()[idx].sg_script_ctx.sc_lnum += sourcing_lnum;
+	HL_TABLE()[idx].sg_script_ctx.sc_lnum += SOURCING_LNUM;
 #endif
     }
 
--- a/src/kword_test.c
+++ b/src/kword_test.c
@@ -76,6 +76,7 @@ test_isword_funcs_utf8(void)
     int
 main(void)
 {
+    estack_init();
     test_isword_funcs_utf8();
     return 0;
 }
--- a/src/main.c
+++ b/src/main.c
@@ -911,6 +911,7 @@ vim_main2(void)
     void
 common_init(mparm_T *paramp)
 {
+    estack_init();
     cmdline_init();
 
     (void)mb_init();	// init mb_bytelen_tab[] to ones
@@ -3089,13 +3090,13 @@ exe_pre_commands(mparm_T *parmp)
     if (cnt > 0)
     {
 	curwin->w_cursor.lnum = 0; // just in case..
-	sourcing_name = (char_u *)_("pre-vimrc command line");
+	estack_push(ETYPE_ARGS, (char_u *)_("pre-vimrc command line"), 0);
 # ifdef FEAT_EVAL
 	current_sctx.sc_sid = SID_CMDARG;
 # endif
 	for (i = 0; i < cnt; ++i)
 	    do_cmdline_cmd(cmds[i]);
-	sourcing_name = NULL;
+	estack_pop();
 # ifdef FEAT_EVAL
 	current_sctx.sc_sid = 0;
 # endif
@@ -3119,7 +3120,7 @@ exe_commands(mparm_T *parmp)
     msg_scroll = TRUE;
     if (parmp->tagname == NULL && curwin->w_cursor.lnum <= 1)
 	curwin->w_cursor.lnum = 0;
-    sourcing_name = (char_u *)"command line";
+    estack_push(ETYPE_ARGS, (char_u *)"command line", 0);
 #ifdef FEAT_EVAL
     current_sctx.sc_sid = SID_CARG;
     current_sctx.sc_seq = 0;
@@ -3130,7 +3131,7 @@ exe_commands(mparm_T *parmp)
 	if (parmp->cmds_tofree[i])
 	    vim_free(parmp->commands[i]);
     }
-    sourcing_name = NULL;
+    estack_pop();
 #ifdef FEAT_EVAL
     current_sctx.sc_sid = 0;
 #endif
@@ -3336,8 +3337,6 @@ process_env(
     int		is_viminit) // when TRUE, called for VIMINIT
 {
     char_u	*initstr;
-    char_u	*save_sourcing_name;
-    linenr_T	save_sourcing_lnum;
 #ifdef FEAT_EVAL
     sctx_T	save_current_sctx;
 #endif
@@ -3346,10 +3345,7 @@ process_env(
     {
 	if (is_viminit)
 	    vimrc_found(NULL, NULL);
-	save_sourcing_name = sourcing_name;
-	save_sourcing_lnum = sourcing_lnum;
-	sourcing_name = env;
-	sourcing_lnum = 0;
+	estack_push(ETYPE_ENV, env, 0);
 #ifdef FEAT_EVAL
 	save_current_sctx = current_sctx;
 	current_sctx.sc_sid = SID_ENV;
@@ -3358,8 +3354,8 @@ process_env(
 	current_sctx.sc_version = 1;
 #endif
 	do_cmdline_cmd(initstr);
-	sourcing_name = save_sourcing_name;
-	sourcing_lnum = save_sourcing_lnum;
+
+	estack_pop();
 #ifdef FEAT_EVAL
 	current_sctx = save_current_sctx;
 #endif
--- a/src/map.c
+++ b/src/map.c
@@ -697,7 +697,7 @@ do_map(
 #ifdef FEAT_EVAL
 				    mp->m_expr = expr;
 				    mp->m_script_ctx = current_sctx;
-				    mp->m_script_ctx.sc_lnum += sourcing_lnum;
+				    mp->m_script_ctx.sc_lnum += SOURCING_LNUM;
 #endif
 				    did_it = TRUE;
 				}
@@ -796,7 +796,7 @@ do_map(
 #ifdef FEAT_EVAL
 	mp->m_expr = expr;
 	mp->m_script_ctx = current_sctx;
-	mp->m_script_ctx.sc_lnum += sourcing_lnum;
+	mp->m_script_ctx.sc_lnum += SOURCING_LNUM;
 #endif
 
 	// add the new entry in front of the abbrlist or maphash[] list
@@ -1915,14 +1915,13 @@ check_map_keycodes(void)
     char_u	*p;
     int		i;
     char_u	buf[3];
-    char_u	*save_name;
     int		abbr;
     int		hash;
     buf_T	*bp;
 
     validate_maphash();
-    save_name = sourcing_name;
-    sourcing_name = (char_u *)"mappings"; // avoids giving error messages
+    // avoids giving error messages
+    estack_push(ETYPE_INTERNAL, (char_u *)"mappings", 0);
 
     // Do this once for each buffer, and then once for global
     // mappings/abbreviations with bp == NULL
@@ -1979,7 +1978,7 @@ check_map_keycodes(void)
 	if (bp == NULL)
 	    break;
     }
-    sourcing_name = save_name;
+    estack_pop();
 }
 
 #if defined(FEAT_EVAL) || defined(PROTO)
--- a/src/message.c
+++ b/src/message.c
@@ -434,15 +434,15 @@ reset_last_sourcing(void)
 }
 
 /*
- * Return TRUE if "sourcing_name" differs from "last_sourcing_name".
+ * Return TRUE if "SOURCING_NAME" differs from "last_sourcing_name".
  */
     static int
 other_sourcing_name(void)
 {
-    if (sourcing_name != NULL)
+    if (SOURCING_NAME != NULL)
     {
 	if (last_sourcing_name != NULL)
-	    return STRCMP(sourcing_name, last_sourcing_name) != 0;
+	    return STRCMP(SOURCING_NAME, last_sourcing_name) != 0;
 	return TRUE;
     }
     return FALSE;
@@ -458,12 +458,19 @@ get_emsg_source(void)
 {
     char_u	*Buf, *p;
 
-    if (sourcing_name != NULL && other_sourcing_name())
+    if (SOURCING_NAME != NULL && other_sourcing_name())
     {
+	char_u	    *sname = estack_sfile();
+	char_u	    *tofree = sname;
+
+	if (sname == NULL)
+	    sname = SOURCING_NAME;
+
 	p = (char_u *)_("Error detected while processing %s:");
-	Buf = alloc(STRLEN(sourcing_name) + STRLEN(p));
+	Buf = alloc(STRLEN(sname) + STRLEN(p));
 	if (Buf != NULL)
-	    sprintf((char *)Buf, (char *)p, sourcing_name);
+	    sprintf((char *)Buf, (char *)p, sname);
+	vim_free(tofree);
 	return Buf;
     }
     return NULL;
@@ -481,14 +488,14 @@ get_emsg_lnum(void)
 
     // lnum is 0 when executing a command from the command line
     // argument, we don't want a line number then
-    if (sourcing_name != NULL
-	    && (other_sourcing_name() || sourcing_lnum != last_sourcing_lnum)
-	    && sourcing_lnum != 0)
+    if (SOURCING_NAME != NULL
+	    && (other_sourcing_name() || SOURCING_LNUM != last_sourcing_lnum)
+	    && SOURCING_LNUM != 0)
     {
 	p = (char_u *)_("line %4ld:");
 	Buf = alloc(STRLEN(p) + 20);
 	if (Buf != NULL)
-	    sprintf((char *)Buf, (char *)p, (long)sourcing_lnum);
+	    sprintf((char *)Buf, (char *)p, (long)SOURCING_LNUM);
 	return Buf;
     }
     return NULL;
@@ -516,17 +523,17 @@ msg_source(int attr)
     {
 	msg_attr((char *)p, HL_ATTR(HLF_N));
 	vim_free(p);
-	last_sourcing_lnum = sourcing_lnum;  // only once for each line
+	last_sourcing_lnum = SOURCING_LNUM;  // only once for each line
     }
 
     // remember the last sourcing name printed, also when it's empty
-    if (sourcing_name == NULL || other_sourcing_name())
+    if (SOURCING_NAME == NULL || other_sourcing_name())
     {
 	vim_free(last_sourcing_name);
-	if (sourcing_name == NULL)
+	if (SOURCING_NAME == NULL)
 	    last_sourcing_name = NULL;
 	else
-	    last_sourcing_name = vim_strsave(sourcing_name);
+	    last_sourcing_name = vim_strsave(SOURCING_NAME);
     }
     --no_wait_return;
 }
@@ -2312,7 +2319,7 @@ inc_msg_scrolled(void)
 #ifdef FEAT_EVAL
     if (*get_vim_var_str(VV_SCROLLSTART) == NUL)
     {
-	char_u	    *p = sourcing_name;
+	char_u	    *p = SOURCING_NAME;
 	char_u	    *tofree = NULL;
 	int	    len;
 
@@ -2327,7 +2334,7 @@ inc_msg_scrolled(void)
 	    if (tofree != NULL)
 	    {
 		vim_snprintf((char *)tofree, len, _("%s line %ld"),
-						      p, (long)sourcing_lnum);
+						      p, (long)SOURCING_LNUM);
 		p = tofree;
 	    }
 	}
--- a/src/option.c
+++ b/src/option.c
@@ -2435,7 +2435,7 @@ set_option_sctx_idx(int opt_idx, int opt
     int		indir = (int)options[opt_idx].indir;
     sctx_T	new_script_ctx = script_ctx;
 
-    new_script_ctx.sc_lnum += sourcing_lnum;
+    new_script_ctx.sc_lnum += SOURCING_LNUM;
 
     // Remember where the option was set.  For local options need to do that
     // in the buffer or window structure.
--- a/src/profiler.c
+++ b/src/profiler.c
@@ -602,10 +602,10 @@ func_line_start(void *cookie)
     funccall_T	*fcp = (funccall_T *)cookie;
     ufunc_T	*fp = fcp->func;
 
-    if (fp->uf_profiling && sourcing_lnum >= 1
-				      && sourcing_lnum <= fp->uf_lines.ga_len)
+    if (fp->uf_profiling && SOURCING_LNUM >= 1
+				      && SOURCING_LNUM <= fp->uf_lines.ga_len)
     {
-	fp->uf_tml_idx = sourcing_lnum - 1;
+	fp->uf_tml_idx = SOURCING_LNUM - 1;
 	// Skip continuation lines.
 	while (fp->uf_tml_idx > 0 && FUNCLINE(fp, fp->uf_tml_idx) == NULL)
 	    --fp->uf_tml_idx;
@@ -906,13 +906,13 @@ script_line_start(void)
     if (current_sctx.sc_sid <= 0 || current_sctx.sc_sid > script_items.ga_len)
 	return;
     si = &SCRIPT_ITEM(current_sctx.sc_sid);
-    if (si->sn_prof_on && sourcing_lnum >= 1)
+    if (si->sn_prof_on && SOURCING_LNUM >= 1)
     {
 	// Grow the array before starting the timer, so that the time spent
 	// here isn't counted.
 	(void)ga_grow(&si->sn_prl_ga,
-				  (int)(sourcing_lnum - si->sn_prl_ga.ga_len));
-	si->sn_prl_idx = sourcing_lnum - 1;
+				  (int)(SOURCING_LNUM - si->sn_prl_ga.ga_len));
+	si->sn_prl_idx = SOURCING_LNUM - 1;
 	while (si->sn_prl_ga.ga_len <= si->sn_prl_idx
 		&& si->sn_prl_ga.ga_len < si->sn_prl_ga.ga_maxlen)
 	{
--- a/src/proto/scriptfile.pro
+++ b/src/proto/scriptfile.pro
@@ -1,4 +1,9 @@
 /* scriptfile.c */
+void estack_init(void);
+estack_T *estack_push(etype_T type, char_u *name, long lnum);
+void estack_push_ufunc(etype_T type, ufunc_T *ufunc, long lnum);
+void estack_pop(void);
+char_u *estack_sfile(void);
 void ex_runtime(exarg_T *eap);
 int do_in_path(char_u *path, char_u *name, int flags, void (*callback)(char_u *fname, void *ck), void *cookie);
 int do_in_runtimepath(char_u *name, int flags, void (*callback)(char_u *fname, void *ck), void *cookie);
--- 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
--- a/src/spellfile.c
+++ b/src/spellfile.c
@@ -349,8 +349,6 @@ spell_load_file(
     int		i;
     int		n;
     int		len;
-    char_u	*save_sourcing_name = sourcing_name;
-    linenr_T	save_sourcing_lnum = sourcing_lnum;
     slang_T	*lp = NULL;
     int		c = 0;
     int		res;
@@ -393,8 +391,7 @@ spell_load_file(
 	lp = old_lp;
 
     // Set sourcing_name, so that error messages mention the file name.
-    sourcing_name = fname;
-    sourcing_lnum = 0;
+    estack_push(ETYPE_SPELL, fname, 0);
 
     /*
      * <HEADER>: <fileID>
@@ -581,8 +578,7 @@ endFAIL:
 endOK:
     if (fd != NULL)
 	fclose(fd);
-    sourcing_name = save_sourcing_name;
-    sourcing_lnum = save_sourcing_lnum;
+    estack_pop();
 
     return lp;
 }
--- a/src/structs.h
+++ b/src/structs.h
@@ -1496,6 +1496,8 @@ typedef struct
 				// used for s: variables
     int		uf_refcount;	// reference count, see func_name_refcount()
     funccall_T	*uf_scoped;	// l: local variables for closure
+    char_u	*uf_name_exp;	// if "uf_name[]" starts with SNR the name with
+				// "<SNR>" as a string, otherwise NULL
     char_u	uf_name[1];	// name of function (actually longer); can
 				// start with <SNR>123_ (<SNR> is K_SPECIAL
 				// KS_EXTRA KE_SNR)
@@ -1665,6 +1667,38 @@ struct partial_S
     dict_T	*pt_dict;	// dict for "self"
 };
 
+typedef struct AutoPatCmd_S AutoPatCmd;
+
+/*
+ * Entry in the execution stack "exestack".
+ */
+typedef enum {
+    ETYPE_TOP,		    // toplevel
+    ETYPE_SCRIPT,           // sourcing script, use es_info.sctx
+    ETYPE_UFUNC,            // user function, use es_info.ufunc
+    ETYPE_AUCMD,            // autocomand, use es_info.aucmd
+    ETYPE_MODELINE,         // modeline, use es_info.sctx
+    ETYPE_EXCEPT,           // exception, use es_info.exception
+    ETYPE_ARGS,             // command line argument
+    ETYPE_ENV,              // environment variable
+    ETYPE_INTERNAL,         // internal operation
+    ETYPE_SPELL,            // loading spell file
+} etype_T;
+
+typedef struct {
+    long      es_lnum;      // replaces "sourcing_lnum"
+    char_u    *es_name;     // replaces "sourcing_name"
+    etype_T   es_type;
+    union {
+	sctx_T  *sctx;      // script and modeline info
+#if defined(FEAT_EVAL)
+	ufunc_T *ufunc;     // function info
+#endif
+	AutoPatCmd *aucmd;  // autocommand info
+	except_T   *except; // exception info
+    } es_info;
+} estack_T;
+
 // Information returned by get_tty_info().
 typedef struct {
     int backspace;	// what the Backspace key produces
--- a/src/term.c
+++ b/src/term.c
@@ -2277,7 +2277,7 @@ add_termcap_entry(char_u *name, int forc
     }
 #endif
 
-    if (sourcing_name == NULL)
+    if (SOURCING_NAME == NULL)
     {
 #ifdef HAVE_TGETENT
 	if (error_msg != NULL)
--- a/src/testdir/test_debugger.vim
+++ b/src/testdir/test_debugger.vim
@@ -14,7 +14,7 @@ func RunDbgCmd(buf, cmd, ...)
     " Verify the expected output
     let lnum = 20 - len(a:1)
     for l in a:1
-      call WaitForAssert({-> assert_equal(l, term_getline(a:buf, lnum))})
+      call WaitForAssert({-> assert_equal(l, term_getline(a:buf, lnum))}, 200)
       let lnum += 1
     endfor
   endif
--- a/src/testing.c
+++ b/src/testing.c
@@ -21,22 +21,24 @@
     static void
 prepare_assert_error(garray_T *gap)
 {
-    char buf[NUMBUFLEN];
+    char    buf[NUMBUFLEN];
+    char_u  *sname = estack_sfile();
 
     ga_init2(gap, 1, 100);
-    if (sourcing_name != NULL)
+    if (sname != NULL)
     {
-	ga_concat(gap, sourcing_name);
-	if (sourcing_lnum > 0)
+	ga_concat(gap, sname);
+	if (SOURCING_LNUM > 0)
 	    ga_concat(gap, (char_u *)" ");
     }
-    if (sourcing_lnum > 0)
+    if (SOURCING_LNUM > 0)
     {
-	sprintf(buf, "line %ld", (long)sourcing_lnum);
+	sprintf(buf, "line %ld", (long)SOURCING_LNUM);
 	ga_concat(gap, (char_u *)buf);
     }
-    if (sourcing_name != NULL || sourcing_lnum > 0)
+    if (sname != NULL || SOURCING_LNUM > 0)
 	ga_concat(gap, (char_u *)": ");
+    vim_free(sname);
 }
 
 /*
--- a/src/usercmd.c
+++ b/src/usercmd.c
@@ -191,7 +191,7 @@ find_ucmd(
 		    {
 			xp->xp_arg = uc->uc_compl_arg;
 			xp->xp_script_ctx = uc->uc_script_ctx;
-			xp->xp_script_ctx.sc_lnum += sourcing_lnum;
+			xp->xp_script_ctx.sc_lnum += SOURCING_LNUM;
 		    }
 # endif
 		    // Do not search for further abbreviations
@@ -956,7 +956,7 @@ uc_add_command(
     cmd->uc_compl = compl;
 #ifdef FEAT_EVAL
     cmd->uc_script_ctx = current_sctx;
-    cmd->uc_script_ctx.sc_lnum += sourcing_lnum;
+    cmd->uc_script_ctx.sc_lnum += SOURCING_LNUM;
     cmd->uc_compl_arg = compl_arg;
 #endif
     cmd->uc_addr_type = addr_type;
--- a/src/userfunc.c
+++ b/src/userfunc.c
@@ -226,6 +226,22 @@ register_closure(ufunc_T *fp)
     return OK;
 }
 
+    static void
+set_ufunc_name(ufunc_T *fp, char_u *name)
+{
+    STRCPY(fp->uf_name, name);
+
+    if (name[0] == K_SPECIAL)
+    {
+	fp->uf_name_exp = alloc(STRLEN(name) + 3);
+	if (fp->uf_name_exp != NULL)
+	{
+	    STRCPY(fp->uf_name_exp, "<SNR>");
+	    STRCAT(fp->uf_name_exp, fp->uf_name + 3);
+	}
+    }
+}
+
 /*
  * Parse a lambda expression and get a Funcref from "*arg".
  * Return OK or FAIL.  Returns NOTDONE for dict or {expr}.
@@ -309,7 +325,7 @@ get_lambda_tv(char_u **arg, typval_T *re
 	vim_strncpy(p + 7, s, e - s);
 
 	fp->uf_refcount = 1;
-	STRCPY(fp->uf_name, name);
+	set_ufunc_name(fp, name);
 	hash_add(&func_hashtab, UF2HIKEY(fp));
 	fp->uf_args = newargs;
 	ga_init(&fp->uf_def_args);
@@ -333,7 +349,7 @@ get_lambda_tv(char_u **arg, typval_T *re
 	fp->uf_flags = flags;
 	fp->uf_calls = 0;
 	fp->uf_script_ctx = current_sctx;
-	fp->uf_script_ctx.sc_lnum += sourcing_lnum - newlines.ga_len;
+	fp->uf_script_ctx.sc_lnum += SOURCING_LNUM - newlines.ga_len;
 
 	pt->pt_func = fp;
 	pt->pt_refcount = 1;
@@ -759,8 +775,6 @@ call_user_func(
     linenr_T	lastline,	// last line of range
     dict_T	*selfdict)	// Dictionary for "self"
 {
-    char_u	*save_sourcing_name;
-    linenr_T	save_sourcing_lnum;
     sctx_T	save_current_sctx;
     int		using_sandbox = FALSE;
     funccall_T	*fc;
@@ -774,7 +788,6 @@ call_user_func(
     int		islambda = FALSE;
     char_u	numbuf[NUMBUFLEN];
     char_u	*name;
-    size_t	len;
 #ifdef FEAT_PROFILE
     proftime_T	wait_start;
     proftime_T	call_start;
@@ -948,9 +961,6 @@ call_user_func(
 
     // Don't redraw while executing the function.
     ++RedrawingDisabled;
-    save_sourcing_name = sourcing_name;
-    save_sourcing_lnum = sourcing_lnum;
-    sourcing_lnum = 1;
 
     if (fp->uf_flags & FC_SANDBOX)
     {
@@ -958,65 +968,51 @@ call_user_func(
 	++sandbox;
     }
 
-    // need space for function name + ("function " + 3) or "[number]"
-    len = (save_sourcing_name == NULL ? 0 : STRLEN(save_sourcing_name))
-						   + STRLEN(fp->uf_name) + 20;
-    sourcing_name = alloc(len);
-    if (sourcing_name != NULL)
+    estack_push_ufunc(ETYPE_UFUNC, fp, 1);
+    if (p_verbose >= 12)
     {
-	if (save_sourcing_name != NULL
-			  && STRNCMP(save_sourcing_name, "function ", 9) == 0)
-	    sprintf((char *)sourcing_name, "%s[%d]..",
-				 save_sourcing_name, (int)save_sourcing_lnum);
-	else
-	    STRCPY(sourcing_name, "function ");
-	cat_func_name(sourcing_name + STRLEN(sourcing_name), fp);
-
-	if (p_verbose >= 12)
+	++no_wait_return;
+	verbose_enter_scroll();
+
+	smsg(_("calling %s"), SOURCING_NAME);
+	if (p_verbose >= 14)
 	{
-	    ++no_wait_return;
-	    verbose_enter_scroll();
-
-	    smsg(_("calling %s"), sourcing_name);
-	    if (p_verbose >= 14)
+	    char_u	buf[MSG_BUF_LEN];
+	    char_u	numbuf2[NUMBUFLEN];
+	    char_u	*tofree;
+	    char_u	*s;
+
+	    msg_puts("(");
+	    for (i = 0; i < argcount; ++i)
 	    {
-		char_u	buf[MSG_BUF_LEN];
-		char_u	numbuf2[NUMBUFLEN];
-		char_u	*tofree;
-		char_u	*s;
-
-		msg_puts("(");
-		for (i = 0; i < argcount; ++i)
+		if (i > 0)
+		    msg_puts(", ");
+		if (argvars[i].v_type == VAR_NUMBER)
+		    msg_outnum((long)argvars[i].vval.v_number);
+		else
 		{
-		    if (i > 0)
-			msg_puts(", ");
-		    if (argvars[i].v_type == VAR_NUMBER)
-			msg_outnum((long)argvars[i].vval.v_number);
-		    else
+		    // Do not want errors such as E724 here.
+		    ++emsg_off;
+		    s = tv2string(&argvars[i], &tofree, numbuf2, 0);
+		    --emsg_off;
+		    if (s != NULL)
 		    {
-			// Do not want errors such as E724 here.
-			++emsg_off;
-			s = tv2string(&argvars[i], &tofree, numbuf2, 0);
-			--emsg_off;
-			if (s != NULL)
+			if (vim_strsize(s) > MSG_BUF_CLEN)
 			{
-			    if (vim_strsize(s) > MSG_BUF_CLEN)
-			    {
-				trunc_string(s, buf, MSG_BUF_CLEN, MSG_BUF_LEN);
-				s = buf;
-			    }
-			    msg_puts((char *)s);
-			    vim_free(tofree);
+			    trunc_string(s, buf, MSG_BUF_CLEN, MSG_BUF_LEN);
+			    s = buf;
 			}
+			msg_puts((char *)s);
+			vim_free(tofree);
 		    }
 		}
-		msg_puts(")");
 	    }
-	    msg_puts("\n");   // don't overwrite this either
-
-	    verbose_leave_scroll();
-	    --no_wait_return;
+	    msg_puts(")");
 	}
+	msg_puts("\n");   // don't overwrite this either
+
+	verbose_leave_scroll();
+	--no_wait_return;
     }
 #ifdef FEAT_PROFILE
     if (do_profiling == PROF_YES)
@@ -1085,9 +1081,9 @@ call_user_func(
 	verbose_enter_scroll();
 
 	if (aborting())
-	    smsg(_("%s aborted"), sourcing_name);
+	    smsg(_("%s aborted"), SOURCING_NAME);
 	else if (fc->rettv->v_type == VAR_NUMBER)
-	    smsg(_("%s returning #%ld"), sourcing_name,
+	    smsg(_("%s returning #%ld"), SOURCING_NAME,
 					       (long)fc->rettv->vval.v_number);
 	else
 	{
@@ -1109,7 +1105,7 @@ call_user_func(
 		    trunc_string(s, buf, MSG_BUF_CLEN, MSG_BUF_LEN);
 		    s = buf;
 		}
-		smsg(_("%s returning %s"), sourcing_name, s);
+		smsg(_("%s returning %s"), SOURCING_NAME, s);
 		vim_free(tofree);
 	    }
 	}
@@ -1119,9 +1115,7 @@ call_user_func(
 	--no_wait_return;
     }
 
-    vim_free(sourcing_name);
-    sourcing_name = save_sourcing_name;
-    sourcing_lnum = save_sourcing_lnum;
+    estack_pop();
     current_sctx = save_current_sctx;
 #ifdef FEAT_PROFILE
     if (do_profiling == PROF_YES)
@@ -1130,12 +1124,12 @@ call_user_func(
     if (using_sandbox)
 	--sandbox;
 
-    if (p_verbose >= 12 && sourcing_name != NULL)
+    if (p_verbose >= 12 && SOURCING_NAME != NULL)
     {
 	++no_wait_return;
 	verbose_enter_scroll();
 
-	smsg(_("continuing in %s"), sourcing_name);
+	smsg(_("continuing in %s"), SOURCING_NAME);
 	msg_puts("\n");   // don't overwrite this either
 
 	verbose_leave_scroll();
@@ -1204,13 +1198,11 @@ func_clear_items(ufunc_T *fp)
     ga_clear_strings(&(fp->uf_args));
     ga_clear_strings(&(fp->uf_def_args));
     ga_clear_strings(&(fp->uf_lines));
+    VIM_CLEAR(fp->uf_name_exp);
 #ifdef FEAT_PROFILE
-    vim_free(fp->uf_tml_count);
-    fp->uf_tml_count = NULL;
-    vim_free(fp->uf_tml_total);
-    fp->uf_tml_total = NULL;
-    vim_free(fp->uf_tml_self);
-    fp->uf_tml_self = NULL;
+    VIM_CLEAR(fp->uf_tml_count);
+    VIM_CLEAR(fp->uf_tml_total);
+    VIM_CLEAR(fp->uf_tml_self);
 #endif
 }
 
@@ -1736,11 +1728,8 @@ list_func_head(ufunc_T *fp, int indent)
     if (indent)
 	msg_puts("   ");
     msg_puts("function ");
-    if (fp->uf_name[0] == K_SPECIAL)
-    {
-	msg_puts_attr("<SNR>", HL_ATTR(HLF_8));
-	msg_puts((char *)fp->uf_name + 3);
-    }
+    if (fp->uf_name_exp != NULL)
+	msg_puts((char *)fp->uf_name_exp);
     else
 	msg_puts((char *)fp->uf_name);
     msg_putchar('(');
@@ -2308,7 +2297,7 @@ ex_function(exarg_T *eap)
     }
 
     // Save the starting line number.
-    sourcing_lnum_top = sourcing_lnum;
+    sourcing_lnum_top = SOURCING_LNUM;
 
     indent = 2;
     nesting = 0;
@@ -2351,10 +2340,10 @@ ex_function(exarg_T *eap)
 	    goto erret;
 	}
 
-	// Detect line continuation: sourcing_lnum increased more than one.
+	// Detect line continuation: SOURCING_LNUM increased more than one.
 	sourcing_lnum_off = get_sourced_lnum(eap->getline, eap->cookie);
-	if (sourcing_lnum < sourcing_lnum_off)
-	    sourcing_lnum_off -= sourcing_lnum;
+	if (SOURCING_LNUM < sourcing_lnum_off)
+	    sourcing_lnum_off -= SOURCING_LNUM;
 	else
 	    sourcing_lnum_off = 0;
 
@@ -2631,16 +2620,16 @@ ex_function(exarg_T *eap)
 
 	    // Check that the autoload name matches the script name.
 	    j = FAIL;
-	    if (sourcing_name != NULL)
+	    if (SOURCING_NAME != NULL)
 	    {
 		scriptname = autoload_name(name);
 		if (scriptname != NULL)
 		{
 		    p = vim_strchr(scriptname, '/');
 		    plen = (int)STRLEN(p);
-		    slen = (int)STRLEN(sourcing_name);
+		    slen = (int)STRLEN(SOURCING_NAME);
 		    if (slen > plen && fnamecmp(p,
-					    sourcing_name + slen - plen) == 0)
+					    SOURCING_NAME + slen - plen) == 0)
 			j = OK;
 		    vim_free(scriptname);
 		}
@@ -2685,7 +2674,7 @@ ex_function(exarg_T *eap)
 	}
 
 	// insert the new function in the function list
-	STRCPY(fp->uf_name, name);
+	set_ufunc_name(fp, name);
 	if (overwrite)
 	{
 	    hi = hash_find(&func_hashtab, name);
@@ -3353,7 +3342,7 @@ get_func_line(
     if (fcp->dbg_tick != debug_tick)
     {
 	fcp->breakpoint = dbg_find_breakpoint(FALSE, fp->uf_name,
-							       sourcing_lnum);
+							       SOURCING_LNUM);
 	fcp->dbg_tick = debug_tick;
     }
 #ifdef FEAT_PROFILE
@@ -3376,7 +3365,7 @@ get_func_line(
 	else
 	{
 	    retval = vim_strsave(((char_u **)(gap->ga_data))[fcp->linenr++]);
-	    sourcing_lnum = fcp->linenr;
+	    SOURCING_LNUM = fcp->linenr;
 #ifdef FEAT_PROFILE
 	    if (do_profiling == PROF_YES)
 		func_line_start(cookie);
@@ -3385,12 +3374,12 @@ get_func_line(
     }
 
     // Did we encounter a breakpoint?
-    if (fcp->breakpoint != 0 && fcp->breakpoint <= sourcing_lnum)
+    if (fcp->breakpoint != 0 && fcp->breakpoint <= SOURCING_LNUM)
     {
-	dbg_breakpoint(fp->uf_name, sourcing_lnum);
+	dbg_breakpoint(fp->uf_name, SOURCING_LNUM);
 	// Find next breakpoint.
 	fcp->breakpoint = dbg_find_breakpoint(FALSE, fp->uf_name,
-							       sourcing_lnum);
+							       SOURCING_LNUM);
 	fcp->dbg_tick = debug_tick;
     }
 
--- a/src/version.c
+++ b/src/version.c
@@ -743,6 +743,8 @@ static char *(features[]) =
 static int included_patches[] =
 {   /* Add new patch number below this line */
 /**/
+    56,
+/**/
     55,
 /**/
     54,