changeset 20538:9f921ba86d05 v8.2.0823

patch 8.2.0823: Vim9: script reload test is disabled Commit: https://github.com/vim/vim/commit/25e0f5863e9010a75a1ff0d04e8f886403968755 Author: Bram Moolenaar <Bram@vim.org> Date: Mon May 25 22:36:50 2020 +0200 patch 8.2.0823: Vim9: script reload test is disabled Problem: Vim9: script reload test is disabled. Solution: Compile a function in the context of the script where it was defined. Set execution stack for compiled function. Add a test that an error is reported for the right file/function.
author Bram Moolenaar <Bram@vim.org>
date Mon, 25 May 2020 22:45:03 +0200
parents cceaa5ec43aa
children ca1354820ef2
files src/ex_docmd.c src/ex_eval.c src/globals.h src/proto/scriptfile.pro src/scriptfile.c src/structs.h src/testdir/test_vim9_script.vim src/userfunc.c src/version.c src/vim9compile.c src/vim9execute.c
diffstat 11 files changed, 125 insertions(+), 41 deletions(-) [+]
line wrap: on
line diff
--- a/src/ex_docmd.c
+++ b/src/ex_docmd.c
@@ -634,8 +634,8 @@ do_cmdline(
     int		*dbg_tick = NULL;	// ptr to dbg_tick field in cookie
     struct dbg_stuff debug_saved;	// saved things for debug mode
     int		initial_trylevel;
-    struct msglist	**saved_msg_list = NULL;
-    struct msglist	*private_msg_list;
+    msglist_T	**saved_msg_list = NULL;
+    msglist_T	*private_msg_list;
 
     // "fgetline" and "cookie" passed to do_one_cmd()
     char_u	*(*cmd_getline)(int, void *, int, int);
@@ -1238,7 +1238,7 @@ do_cmdline(
 	if (did_throw)
 	{
 	    void	*p = NULL;
-	    struct msglist	*messages = NULL, *next;
+	    msglist_T	*messages = NULL, *next;
 
 	    /*
 	     * If the uncaught exception is a user exception, report it as an
--- a/src/ex_eval.c
+++ b/src/ex_eval.c
@@ -146,8 +146,8 @@ cause_errthrow(
     int		severe,
     int		*ignore)
 {
-    struct msglist *elem;
-    struct msglist **plist;
+    msglist_T	*elem;
+    msglist_T	**plist;
 
     /*
      * Do nothing when displaying the interrupt message or reporting an
@@ -251,7 +251,7 @@ cause_errthrow(
 	    while (*plist != NULL)
 		plist = &(*plist)->next;
 
-	    elem = ALLOC_ONE(struct msglist);
+	    elem = ALLOC_CLEAR_ONE(msglist_T);
 	    if (elem == NULL)
 	    {
 		suppress_errthrow = TRUE;
@@ -287,6 +287,11 @@ cause_errthrow(
 			else
 			    (*msg_list)->throw_msg = tmsg;
 		    }
+
+		    // Get the source name and lnum now, it may change before
+		    // reaching do_errthrow().
+		    elem->sfile = estack_sfile();
+		    elem->slnum = SOURCING_LNUM;
 		}
 	    }
 	}
@@ -298,15 +303,16 @@ cause_errthrow(
  * Free a "msg_list" and the messages it contains.
  */
     static void
-free_msglist(struct msglist *l)
+free_msglist(msglist_T *l)
 {
-    struct msglist  *messages, *next;
+    msglist_T  *messages, *next;
 
     messages = l;
     while (messages != NULL)
     {
 	next = messages->next;
 	vim_free(messages->msg);
+	vim_free(messages->sfile);
 	vim_free(messages);
 	messages = next;
     }
@@ -428,7 +434,7 @@ get_exception_string(
     if (type == ET_ERROR)
     {
 	*should_free = TRUE;
-	mesg = ((struct msglist *)value)->throw_msg;
+	mesg = ((msglist_T *)value)->throw_msg;
 	if (cmdname != NULL && *cmdname != NUL)
 	{
 	    cmdlen = (int)STRLEN(cmdname);
@@ -526,23 +532,34 @@ throw_exception(void *value, except_type
     if (type == ET_ERROR)
 	// Store the original message and prefix the exception value with
 	// "Vim:" or, if a command name is given, "Vim(cmdname):".
-	excp->messages = (struct msglist *)value;
+	excp->messages = (msglist_T *)value;
 
     excp->value = get_exception_string(value, type, cmdname, &should_free);
     if (excp->value == NULL && should_free)
 	goto nomem;
 
     excp->type = type;
-    excp->throw_name = estack_sfile();
-    if (excp->throw_name == NULL)
-	excp->throw_name = vim_strsave((char_u *)"");
-    if (excp->throw_name == NULL)
+    if (type == ET_ERROR && ((msglist_T *)value)->sfile != NULL)
+    {
+	msglist_T *entry = (msglist_T *)value;
+
+	excp->throw_name = entry->sfile;
+	entry->sfile = NULL;
+	excp->throw_lnum = entry->slnum;
+    }
+    else
     {
-	if (should_free)
-	    vim_free(excp->value);
-	goto nomem;
+	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
@@ -344,7 +344,7 @@ EXTERN int force_abort INIT(= FALSE);
  * field of a later list element, when the "emsg_severe" flag was set when the
  * emsg() call was made.
  */
-EXTERN struct msglist **msg_list INIT(= NULL);
+EXTERN msglist_T **msg_list INIT(= NULL);
 
 /*
  * suppress_errthrow: When TRUE, don't convert an error to an exception.  Used
--- a/src/proto/scriptfile.pro
+++ b/src/proto/scriptfile.pro
@@ -1,7 +1,8 @@
 /* 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_push_ufunc(ufunc_T *ufunc, long lnum);
+int estack_top_is_ufunc(ufunc_T *ufunc, long lnum);
 void estack_pop(void);
 char_u *estack_sfile(void);
 void ex_runtime(exarg_T *eap);
--- a/src/scriptfile.c
+++ b/src/scriptfile.c
@@ -69,14 +69,31 @@ estack_push(etype_T type, char_u *name, 
  * Add a user function to the execution stack.
  */
     void
-estack_push_ufunc(etype_T type, ufunc_T *ufunc, long lnum)
+estack_push_ufunc(ufunc_T *ufunc, long lnum)
 {
-    estack_T *entry = estack_push(type,
+    estack_T *entry = estack_push(ETYPE_UFUNC,
 	    ufunc->uf_name_exp != NULL
 				  ? ufunc->uf_name_exp : ufunc->uf_name, lnum);
     if (entry != NULL)
 	entry->es_info.ufunc = ufunc;
 }
+
+/*
+ * Return TRUE if "ufunc" with "lnum" is already at the top of the exe stack.
+ */
+    int
+estack_top_is_ufunc(ufunc_T *ufunc, long lnum)
+{
+    estack_T *entry;
+
+    if (exestack.ga_len == 0)
+	return FALSE;
+    entry = ((estack_T *)exestack.ga_data) + exestack.ga_len - 1;
+    return entry->es_type == ETYPE_UFUNC
+	&& STRCMP( entry->es_name, ufunc->uf_name_exp != NULL
+				    ? ufunc->uf_name_exp : ufunc->uf_name) == 0
+	&& entry->es_lnum == lnum;
+}
 #endif
 
 /*
--- a/src/structs.h
+++ b/src/structs.h
@@ -927,13 +927,16 @@ typedef struct {
  * A list of error messages that can be converted to an exception.  "throw_msg"
  * is only set in the first element of the list.  Usually, it points to the
  * original message stored in that element, but sometimes it points to a later
- * message in the list.  See cause_errthrow() below.
+ * message in the list.  See cause_errthrow().
  */
+typedef struct msglist msglist_T;
 struct msglist
 {
-    char		*msg;		// original message
-    char		*throw_msg;	// msg to throw: usually original one
-    struct msglist	*next;		// next of several messages in a row
+    char	*msg;		// original message, allocated
+    char	*throw_msg;	// msg to throw: usually original one
+    char_u	*sfile;		// value from estack_sfile(), allocated
+    long	slnum;		// line number for "sfile"
+    msglist_T	*next;		// next of several messages in a row
 };
 
 /*
@@ -1516,6 +1519,7 @@ struct blobvar_S
 #if defined(FEAT_EVAL) || defined(PROTO)
 typedef struct funccall_S funccall_T;
 
+// values used for "uf_dfunc_idx"
 # define UF_NOT_COMPILED -2
 # define UF_TO_BE_COMPILED -1
 
--- a/src/testdir/test_vim9_script.vim
+++ b/src/testdir/test_vim9_script.vim
@@ -745,9 +745,6 @@ def Test_vim9script_fails()
 enddef
 
 def Test_vim9script_reload_import()
-  " TODO: make it work to compile when not in the script context anymore
-  return
-
   let lines =<< trim END
     vim9script
     const var = ''
@@ -797,9 +794,6 @@ def Test_vim9script_reload_import()
 enddef
 
 def Test_vim9script_reload_delfunc()
-  " TODO: make it work to compile when not in the script context anymore
-  return
-
   let first_lines =<< trim END
     vim9script
     def FuncYes(): string
@@ -920,6 +914,37 @@ def Test_import_rtp()
   delete('import', 'rf')
 enddef
 
+def Test_import_compile_error()
+  let export_lines = [
+        'vim9script',
+        'export def ExpFunc(): string',
+        '  return notDefined',
+        'enddef',
+        ]
+  writefile(export_lines, 'Xexported.vim')
+
+  let import_lines = [
+        'vim9script',
+        'import ExpFunc from "./Xexported.vim"',
+        'def ImpFunc()',
+        '  echo ExpFunc()',
+        'enddef',
+        'defcompile',
+        ]
+  writefile(import_lines, 'Ximport.vim')
+
+  try
+    source Ximport.vim
+  catch /E1001/
+    " Error should be fore the Xexported.vim file.
+    assert_match('E1001: variable not found: notDefined', v:exception)
+    assert_match('function <SNR>\d\+_ImpFunc\[1\]..<SNR>\d\+_ExpFunc, line 1', v:throwpoint)
+  endtry
+
+  delete('Xexported.vim')
+  delete('Ximport.vim')
+enddef
+
 def Test_fixed_size_list()
   " will be allocated as one piece of memory, check that changes work
   let l = [1, 2, 3, 4]
--- a/src/userfunc.c
+++ b/src/userfunc.c
@@ -1114,11 +1114,11 @@ call_user_func(
 
     if (fp->uf_dfunc_idx != UF_NOT_COMPILED)
     {
-	estack_push_ufunc(ETYPE_UFUNC, fp, 1);
+	estack_push_ufunc(fp, 1);
 	save_current_sctx = current_sctx;
 	current_sctx = fp->uf_script_ctx;
 
-	// Execute the compiled function.
+	// Execute the function, possibly compiling it first.
 	call_def_function(fp, argcount, argvars, funcexe->partial, rettv);
 	--depth;
 	current_funccal = fc->caller;
@@ -1288,7 +1288,7 @@ call_user_func(
 	++sandbox;
     }
 
-    estack_push_ufunc(ETYPE_UFUNC, fp, 1);
+    estack_push_ufunc(fp, 1);
     ESTACK_CHECK_SETUP
     if (p_verbose >= 12)
     {
--- a/src/version.c
+++ b/src/version.c
@@ -747,6 +747,8 @@ static char *(features[]) =
 static int included_patches[] =
 {   /* Add new patch number below this line */
 /**/
+    823,
+/**/
     822,
 /**/
     821,
--- a/src/vim9compile.c
+++ b/src/vim9compile.c
@@ -2323,8 +2323,7 @@ next_line_from_context(cctx_T *cctx)
 	}
 	line = ((char_u **)cctx->ctx_ufunc->uf_lines.ga_data)[cctx->ctx_lnum];
 	cctx->ctx_line_start = line;
-	SOURCING_LNUM = cctx->ctx_ufunc->uf_script_ctx.sc_lnum
-							  + cctx->ctx_lnum + 1;
+	SOURCING_LNUM = cctx->ctx_lnum + 1;
     } while (line == NULL || *skipwhite(line) == NUL);
     return line;
 }
@@ -6349,14 +6348,15 @@ compile_def_function(ufunc_T *ufunc, int
     int		called_emsg_before = called_emsg;
     int		ret = FAIL;
     sctx_T	save_current_sctx = current_sctx;
+    int		do_estack_push;
     int		emsg_before = called_emsg;
 
+    // When using a function that was compiled before: Free old instructions.
+    // Otherwise add a new entry in "def_functions".
     if (ufunc->uf_dfunc_idx >= 0)
     {
-	// Redefining a function that was compiled before.
 	dfunc_T *dfunc = ((dfunc_T *)def_functions.ga_data)
 							 + ufunc->uf_dfunc_idx;
-	// Free old instructions.
 	delete_def_function_contents(dfunc);
     }
     else if (add_def_function(ufunc) == FAIL)
@@ -6373,9 +6373,17 @@ compile_def_function(ufunc_T *ufunc, int
     ga_init2(&cctx.ctx_instr, sizeof(isn_T), 50);
     instr = &cctx.ctx_instr;
 
-    // Most modern script version.
+    // Set the context to the function, it may be compiled when called from
+    // another script.  Set the script version to the most modern one.
+    // The line number will be set in next_line_from_context().
+    current_sctx = ufunc->uf_script_ctx;
     current_sctx.sc_version = SCRIPT_VERSION_VIM9;
 
+    // Make sure error messages are OK.
+    do_estack_push = !estack_top_is_ufunc(ufunc, 1);
+    if (do_estack_push)
+	estack_push_ufunc(ufunc, 1);
+
     if (ufunc->uf_def_args.ga_len > 0)
     {
 	int	count = ufunc->uf_def_args.ga_len;
@@ -6795,6 +6803,9 @@ erret:
     }
 
     current_sctx = save_current_sctx;
+    if (do_estack_push)
+	estack_pop();
+
     free_imported(&cctx);
     free_locals(&cctx);
     ga_clear(&cctx.ctx_type_stack);
--- a/src/vim9execute.c
+++ b/src/vim9execute.c
@@ -230,7 +230,7 @@ call_dfunc(int cdf_idx, int argcount_arg
     // Set execution state to the start of the called function.
     ectx->ec_dfunc_idx = cdf_idx;
     ectx->ec_instr = dfunc->df_instr;
-    estack_push_ufunc(ETYPE_UFUNC, dfunc->df_ufunc, 1);
+    estack_push_ufunc(dfunc->df_ufunc, 1);
 
     // Decide where to start execution, handles optional arguments.
     init_instr_idx(ufunc, argcount, ectx);
@@ -656,6 +656,7 @@ call_def_function(
     int		defcount = ufunc->uf_args.ga_len - argc;
     int		save_sc_version = current_sctx.sc_version;
     int		breakcheck_count = 0;
+    int		called_emsg_before = called_emsg;
 
 // Get pointer to item in the stack.
 #define STACK_TV(idx) (((typval_T *)ectx.ec_stack.ga_data) + idx)
@@ -673,7 +674,13 @@ call_def_function(
     if (ufunc->uf_dfunc_idx == UF_NOT_COMPILED
 	    || (ufunc->uf_dfunc_idx == UF_TO_BE_COMPILED
 			  && compile_def_function(ufunc, FALSE, NULL) == FAIL))
+    {
+	if (called_emsg == called_emsg_before)
+	    semsg(_("E1091: Function is not compiled: %s"),
+		    ufunc->uf_name_exp == NULL
+					? ufunc->uf_name : ufunc->uf_name_exp);
 	return FAIL;
+    }
 
     {
 	// Check the function was really compiled.