# HG changeset patch # User Bram Moolenaar # Date 1594215004 -7200 # Node ID caab594592cc3b26e5d5bb426b14d957fcc23229 # Parent d7f16d42548f7f1d97e5d1ae57d6418b6bc0ef21 patch 8.2.1154: Vim9: crash when using imported function Commit: https://github.com/vim/vim/commit/c620c055ce8505596a7208ba696a32b8a3be4f4b Author: Bram Moolenaar 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) diff --git a/src/evalvars.c b/src/evalvars.c --- 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; diff --git a/src/proto/scriptfile.pro b/src/proto/scriptfile.pro --- 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); diff --git a/src/scriptfile.c b/src/scriptfile.c --- 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; } /* diff --git a/src/structs.h b/src/structs.h --- 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(). diff --git a/src/testdir/test_vim9_script.vim b/src/testdir/test_vim9_script.vim --- 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 + 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 = 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 diff --git a/src/version.c b/src/version.c --- 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, diff --git a/src/vim9execute.c b/src/vim9execute.c --- 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;