patch 9.1.0984: exception handling can be improved

Problem:  exception handling can be improved
Solution: add v:stacktrace and getstacktrace()

closes: #16360

Co-authored-by: Naruhiko Nishino <naru123456789@gmail.com>
Signed-off-by: ichizok <gclient.gaap@gmail.com>
Signed-off-by: Christian Brabandt <cb@256bit.org>
diff --git a/src/scriptfile.c b/src/scriptfile.c
index 711f576..856d00f 100644
--- a/src/scriptfile.c
+++ b/src/scriptfile.c
@@ -237,6 +237,89 @@
 #endif
 }
 
+#ifdef FEAT_EVAL
+    static void
+stacktrace_push_item(
+	list_T		*l,
+	ufunc_T		*fp,
+	char_u		*event,
+	linenr_T	lnum,
+	char_u		*filepath)
+{
+    dict_T	*d;
+    typval_T	tv;
+
+    d = dict_alloc_lock(VAR_FIXED);
+    if (d == NULL)
+	return;
+
+    tv.v_type = VAR_DICT;
+    tv.v_lock = VAR_LOCKED;
+    tv.vval.v_dict = d;
+
+    if (fp != NULL)
+	dict_add_func(d, "funcref", fp);
+    if (event != NULL)
+	dict_add_string(d, "event", event);
+    dict_add_number(d, "lnum", lnum);
+    dict_add_string(d, "filepath", filepath);
+
+    list_append_tv(l, &tv);
+}
+
+/*
+ * Create the stacktrace from exestack.
+ */
+    list_T *
+stacktrace_create(void)
+{
+    list_T	*l;
+    int		i;
+
+    l = list_alloc();
+    if (l == NULL)
+	return NULL;
+
+    for (i = 0; i < exestack.ga_len; ++i)
+    {
+	estack_T *entry = &((estack_T *)exestack.ga_data)[i];
+	linenr_T lnum = entry->es_lnum;
+
+	if (entry->es_type == ETYPE_SCRIPT)
+	    stacktrace_push_item(l, NULL, NULL, lnum, entry->es_name);
+	else if (entry->es_type == ETYPE_UFUNC)
+	{
+	    ufunc_T *fp = entry->es_info.ufunc;
+	    sctx_T sctx = fp->uf_script_ctx;
+	    char_u *filepath = sctx.sc_sid > 0 ?
+				   get_scriptname(sctx.sc_sid) : (char_u *)"";
+
+	    lnum += sctx.sc_lnum;
+	    stacktrace_push_item(l, fp, NULL, lnum, filepath);
+	}
+	else if (entry->es_type == ETYPE_AUCMD)
+	{
+	    sctx_T sctx = *acp_script_ctx(entry->es_info.aucmd);
+	    char_u *filepath = sctx.sc_sid > 0 ?
+				   get_scriptname(sctx.sc_sid) : (char_u *)"";
+
+	    lnum += sctx.sc_lnum;
+	    stacktrace_push_item(l, NULL, entry->es_name, lnum, filepath);
+	}
+    }
+    return l;
+}
+
+/*
+ * getstacktrace() function
+ */
+    void
+f_getstacktrace(typval_T *argvars UNUSED, typval_T *rettv)
+{
+    rettv_list_set(rettv, stacktrace_create());
+}
+#endif
+
 /*
  * Get DIP_ flags from the [where] argument of a :runtime command.
  * "*argp" is advanced to after the [where] argument if it is found.