changeset 21206:caab594592cc v8.2.1154

patch 8.2.1154: Vim9: crash when using imported function Commit: https://github.com/vim/vim/commit/c620c055ce8505596a7208ba696a32b8a3be4f4b Author: Bram Moolenaar <Bram@vim.org> Date: Wed Jul 8 15:16:19 2020 +0200 patch 8.2.1154: Vim9: crash when using imported function Problem: Vim9: crash when using imported function. Solution: Check for a function type. Set the script context when calling a function. (closes #6412)
author Bram Moolenaar <Bram@vim.org>
date Wed, 08 Jul 2020 15:30:04 +0200
parents d7f16d42548f
children 2a1156d4950f
files src/evalvars.c src/proto/scriptfile.pro src/scriptfile.c src/structs.h src/testdir/test_vim9_script.vim src/version.c src/vim9execute.c
diffstat 7 files changed, 84 insertions(+), 20 deletions(-) [+]
line wrap: on
line diff
--- a/src/evalvars.c
+++ b/src/evalvars.c
@@ -2375,6 +2375,7 @@ eval_variable(
 {
     int		ret = OK;
     typval_T	*tv = NULL;
+    int		foundFunc = FALSE;
     dictitem_T	*v;
     int		cc;
 
@@ -2402,21 +2403,36 @@ eval_variable(
 	// imported variable from another script
 	if (import != NULL)
 	{
-	    scriptitem_T    *si = SCRIPT_ITEM(import->imp_sid);
-	    svar_T	    *sv = ((svar_T *)si->sn_var_vals.ga_data)
+	    if (import->imp_funcname != NULL)
+	    {
+		foundFunc = TRUE;
+		if (rettv != NULL)
+		{
+		    rettv->v_type = VAR_FUNC;
+		    rettv->vval.v_string = vim_strsave(import->imp_funcname);
+		}
+	    }
+	    else
+	    {
+		scriptitem_T    *si = SCRIPT_ITEM(import->imp_sid);
+		svar_T	    *sv = ((svar_T *)si->sn_var_vals.ga_data)
 						    + import->imp_var_vals_idx;
-	    tv = sv->sv_tv;
+		tv = sv->sv_tv;
+	    }
 	}
     }
 
-    if (tv == NULL)
+    if (!foundFunc)
     {
-	if (rettv != NULL && verbose)
-	    semsg(_(e_undefvar), name);
-	ret = FAIL;
+	if (tv == NULL)
+	{
+	    if (rettv != NULL && verbose)
+		semsg(_(e_undefvar), name);
+	    ret = FAIL;
+	}
+	else if (rettv != NULL)
+	    copy_tv(tv, rettv);
     }
-    else if (rettv != NULL)
-	copy_tv(tv, rettv);
 
     name[len] = cc;
 
--- a/src/proto/scriptfile.pro
+++ b/src/proto/scriptfile.pro
@@ -1,9 +1,9 @@
 /* scriptfile.c */
 void estack_init(void);
 estack_T *estack_push(etype_T type, char_u *name, long lnum);
-void estack_push_ufunc(ufunc_T *ufunc, long lnum);
+estack_T *estack_push_ufunc(ufunc_T *ufunc, long lnum);
 int estack_top_is_ufunc(ufunc_T *ufunc, long lnum);
-void estack_pop(void);
+estack_T *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);
--- a/src/scriptfile.c
+++ b/src/scriptfile.c
@@ -68,7 +68,7 @@ estack_push(etype_T type, char_u *name, 
 /*
  * Add a user function to the execution stack.
  */
-    void
+    estack_T *
 estack_push_ufunc(ufunc_T *ufunc, long lnum)
 {
     estack_T *entry = estack_push(ETYPE_UFUNC,
@@ -76,6 +76,7 @@ estack_push_ufunc(ufunc_T *ufunc, long l
 				  ? ufunc->uf_name_exp : ufunc->uf_name, lnum);
     if (entry != NULL)
 	entry->es_info.ufunc = ufunc;
+    return entry;
 }
 
 /*
@@ -97,13 +98,15 @@ estack_top_is_ufunc(ufunc_T *ufunc, long
 #endif
 
 /*
- * Take an item off of the execution stack.
+ * Take an item off of the execution stack and return it.
  */
-    void
+    estack_T *
 estack_pop(void)
 {
-    if (exestack.ga_len > 1)
-	--exestack.ga_len;
+    if (exestack.ga_len == 0)
+	return NULL;
+    --exestack.ga_len;
+    return ((estack_T *)exestack.ga_data) + exestack.ga_len;
 }
 
 /*
--- a/src/structs.h
+++ b/src/structs.h
@@ -1905,6 +1905,9 @@ typedef struct {
 	AutoPatCmd *aucmd;  // autocommand info
 	except_T   *except; // exception info
     } es_info;
+#if defined(FEAT_EVAL)
+    scid_T	es_save_sid;	    // saved sc_sid when calling function
+#endif
 } estack_T;
 
 // Information returned by get_tty_info().
--- a/src/testdir/test_vim9_script.vim
+++ b/src/testdir/test_vim9_script.vim
@@ -911,10 +911,10 @@ func Test_import_fails_without_script()
   CheckRunVimInTerminal
 
   " call indirectly to avoid compilation error for missing functions
-  call Run_Test_import_fails_without_script()
+  call Run_Test_import_fails_on_command_line()
 endfunc
 
-def Run_Test_import_fails_without_script()
+def Run_Test_import_fails_on_command_line()
   let export =<< trim END
     vim9script
     export def Foo(): number
@@ -1013,6 +1013,35 @@ def Test_vim9script_funcref()
   delete('Xscript.vim')
 enddef
 
+" Check that when searcing for "FilterFunc" it doesn't find the import in the
+" script where FastFilter() is called from.
+def Test_vim9script_funcref_other_script()
+  let filterLines =<< trim END
+    vim9script
+    export def FilterFunc(idx: number, val: number): bool
+      return idx % 2 == 1
+    enddef
+    export def FastFilter(): list<number>
+      return range(10)->filter('FilterFunc')
+    enddef
+  END
+  writefile(filterLines, 'Xfilter.vim')
+
+  let lines =<< trim END
+    vim9script
+    import {FilterFunc, FastFilter} from './Xfilter.vim'
+    def Test()
+      let x: list<number> = FastFilter()
+    enddef
+    Test()
+  END
+  writefile(lines, 'Ximport.vim')
+  assert_fails('source Ximport.vim', 'E121:')
+
+  delete('Xfilter.vim')
+  delete('Ximport.vim')
+enddef
+
 def Test_vim9script_reload_delfunc()
   let first_lines =<< trim END
     vim9script
--- a/src/version.c
+++ b/src/version.c
@@ -755,6 +755,8 @@ static char *(features[]) =
 static int included_patches[] =
 {   /* Add new patch number below this line */
 /**/
+    1154,
+/**/
     1153,
 /**/
     1152,
--- a/src/vim9execute.c
+++ b/src/vim9execute.c
@@ -160,6 +160,7 @@ call_dfunc(int cdf_idx, int argcount_arg
     int	    arg_to_add;
     int	    vararg_count = 0;
     int	    idx;
+    estack_T *entry;
 
     if (dfunc->df_deleted)
     {
@@ -230,7 +231,14 @@ 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(dfunc->df_ufunc, 1);
+    entry = estack_push_ufunc(dfunc->df_ufunc, 1);
+    if (entry != NULL)
+    {
+	// Set the script context to the script where the function was defined.
+	// TODO: save more than the SID?
+	entry->es_save_sid = current_sctx.sc_sid;
+	current_sctx.sc_sid = ufunc->uf_script_ctx.sc_sid;
+    }
 
     // Decide where to start execution, handles optional arguments.
     init_instr_idx(ufunc, argcount, ectx);
@@ -386,9 +394,12 @@ func_return(ectx_T *ectx)
 							  + ectx->ec_dfunc_idx;
     int		argcount = ufunc_argcount(dfunc->df_ufunc);
     int		top = ectx->ec_frame_idx - argcount;
+    estack_T	*entry;
 
     // execution context goes one level up
-    estack_pop();
+    entry = estack_pop();
+    if (entry != NULL)
+	current_sctx.sc_sid = entry->es_save_sid;
 
     if (handle_closure_in_use(ectx, TRUE) == FAIL)
 	return FAIL;