# HG changeset patch # User Bram Moolenaar # Date 1648671304 -7200 # Node ID 4b322951ebacb8c5652056bd66d171bcbc9fdf0c # Parent 08964a0fe11109982eaced2c6a77fcc6662ed911 patch 8.2.4650: "import autoload" only works with using 'runtimepath' Commit: https://github.com/vim/vim/commit/c0ceeeb839b8c6bebd3a2abd1c07d40ec3c6edca Author: Bram Moolenaar Date: Wed Mar 30 21:12:27 2022 +0100 patch 8.2.4650: "import autoload" only works with using 'runtimepath' Problem: "import autoload" only works with using 'runtimepath'. Solution: Also support a relative and absolute file name. diff --git a/src/proto/scriptfile.pro b/src/proto/scriptfile.pro --- a/src/proto/scriptfile.pro +++ b/src/proto/scriptfile.pro @@ -6,6 +6,8 @@ int estack_top_is_ufunc(ufunc_T *ufunc, estack_T *estack_pop(void); char_u *estack_sfile(estack_arg_T which); void ex_runtime(exarg_T *eap); +int find_script_by_name(char_u *name); +int get_new_scriptitem_for_fname(int *error, char_u *fname); 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); int source_runtime(char_u *name, int flags); diff --git a/src/proto/userfunc.pro b/src/proto/userfunc.pro --- a/src/proto/userfunc.pro +++ b/src/proto/userfunc.pro @@ -8,6 +8,7 @@ char_u *deref_func_name(char_u *name, in void emsg_funcname(char *ermsg, char_u *name); int get_func_tv(char_u *name, int len, typval_T *rettv, char_u **arg, evalarg_T *evalarg, funcexe_T *funcexe); char_u *fname_trans_sid(char_u *name, char_u *fname_buf, char_u **tofree, int *error); +void func_name_with_sid(char_u *name, int sid, char_u *buffer); ufunc_T *find_func_even_dead(char_u *name, int flags); ufunc_T *find_func(char_u *name, int is_global); int func_is_global(ufunc_T *ufunc); diff --git a/src/proto/vim9instr.pro b/src/proto/vim9instr.pro --- a/src/proto/vim9instr.pro +++ b/src/proto/vim9instr.pro @@ -54,6 +54,7 @@ int generate_PCALL(cctx_T *cctx, int arg int generate_STRINGMEMBER(cctx_T *cctx, char_u *name, size_t len); int generate_ECHO(cctx_T *cctx, int with_white, int count); int generate_MULT_EXPR(cctx_T *cctx, isntype_T isn_type, int count); +int generate_SOURCE(cctx_T *cctx, int sid); int generate_PUT(cctx_T *cctx, int regname, linenr_T lnum); int generate_EXEC_copy(cctx_T *cctx, isntype_T isntype, char_u *line); int generate_EXEC(cctx_T *cctx, isntype_T isntype, char_u *str); diff --git a/src/scriptfile.c b/src/scriptfile.c --- a/src/scriptfile.c +++ b/src/scriptfile.c @@ -251,7 +251,7 @@ source_callback(char_u *fname, void *coo * Find an already loaded script "name". * If found returns its script ID. If not found returns -1. */ - static int + int find_script_by_name(char_u *name) { int sid; @@ -320,6 +320,21 @@ get_new_scriptitem(int *error) return sid; } + int +get_new_scriptitem_for_fname(int *error, char_u *fname) +{ + int sid = get_new_scriptitem(error); + + if (*error == OK) + { + scriptitem_T *si = SCRIPT_ITEM(sid); + + si->sn_name = vim_strsave(fname); + si->sn_state = SN_STATE_NOT_LOADED; + } + return sid; +} + static void find_script_callback(char_u *fname, void *cookie) { @@ -329,17 +344,8 @@ find_script_callback(char_u *fname, void sid = find_script_by_name(fname); if (sid < 0) - { // script does not exist yet, create a new scriptitem - sid = get_new_scriptitem(&error); - if (error == OK) - { - scriptitem_T *si = SCRIPT_ITEM(sid); - - si->sn_name = vim_strsave(fname); - si->sn_state = SN_STATE_NOT_LOADED; - } - } + sid = get_new_scriptitem_for_fname(&error, fname); *ret_sid = sid; } #endif diff --git a/src/structs.h b/src/structs.h --- a/src/structs.h +++ b/src/structs.h @@ -1833,7 +1833,7 @@ typedef struct { */ typedef struct { - char_u *sn_name; + char_u *sn_name; // full path of script file int sn_script_seq; // latest sctx_T sc_seq value // "sn_vars" stores the s: variables currently valid. When leaving a block @@ -1864,9 +1864,12 @@ typedef struct char_u *sn_save_cpo; // 'cpo' value when :vim9script found char sn_is_vimrc; // .vimrc file, do not restore 'cpo' - // for "vim9script autoload" this is "dir#scriptname#" + // for a Vim9 script under "rtp/autoload/" this is "dir#scriptname#" char_u *sn_autoload_prefix; + // TRUE for a script used with "import autoload './dirname/script.vim'" + int sn_import_autoload; + # ifdef FEAT_PROFILE int sn_prof_on; // TRUE when script is/was profiled int sn_pr_force; // forceit: profile functions in this script diff --git a/src/testdir/test_vim9_disassemble.vim b/src/testdir/test_vim9_disassemble.vim --- a/src/testdir/test_vim9_disassemble.vim +++ b/src/testdir/test_vim9_disassemble.vim @@ -318,6 +318,46 @@ def Test_disassemble_push() &rtp = save_rtp enddef +def Test_disassemble_import_autoload() + writefile(['vim9script'], 'XimportAL.vim') + + var lines =<< trim END + vim9script + import autoload './XimportAL.vim' + + def AutoloadFunc() + echo XimportAL.SomeFunc() + echo XimportAL.someVar + XimportAL.someVar = "yes" + enddef + + var res = execute('disass AutoloadFunc') + assert_match('\d*_AutoloadFunc.*' .. + 'echo XimportAL.SomeFunc()\_s*' .. + '\d SOURCE /home/mool/vim/vim82/src/testdir/XimportAL.vim\_s*' .. + '\d PUSHFUNC "<80>R\d\+_SomeFunc"\_s*' .. + '\d PCALL top (argc 0)\_s*' .. + '\d PCALL end\_s*' .. + '\d ECHO 1\_s*' .. + + 'echo XimportAL.someVar\_s*' .. + '\d SOURCE .*/testdir/XimportAL.vim\_s*' .. + '\d LOADEXPORT s:someVar from .*/testdir/XimportAL.vim\_s*' .. + '\d ECHO 1\_s*' .. + + 'XimportAL.someVar = "yes"\_s*' .. + '\d\+ PUSHS "yes"\_s*' .. + '\d\+ SOURCE .*/testdir/XimportAL.vim\_s*' .. + '\d\+ STOREEXPORT someVar in .*/testdir/XimportAL.vim\_s*' .. + + '\d\+ RETURN void', + res) + END + v9.CheckScriptSuccess(lines) + + delete('XimportAL.vim') +enddef + def s:ScriptFuncStore() var localnr = 1 localnr = 2 diff --git a/src/testdir/test_vim9_import.vim b/src/testdir/test_vim9_import.vim --- a/src/testdir/test_vim9_import.vim +++ b/src/testdir/test_vim9_import.vim @@ -840,6 +840,135 @@ def Test_use_autoload_import_in_fold_exp &rtp = save_rtp enddef +def Test_autoload_import_relative() + var lines =<< trim END + vim9script + + g:loaded = 'yes' + export def RelFunc(): string + return 'relfunc' + enddef + def NotExported() + echo 'not' + enddef + + export var someText = 'some text' + var notexp = 'bad' + END + writefile(lines, 'XimportRel.vim') + writefile(lines, 'XimportRel2.vim') + writefile(lines, 'XimportRel3.vim') + + lines =<< trim END + vim9script + g:loaded = 'no' + import autoload './XimportRel.vim' + assert_equal('no', g:loaded) + + def AFunc(): string + var res = '' + res ..= XimportRel.RelFunc() + res ..= '/' + res ..= XimportRel.someText + XimportRel.someText = 'from AFunc' + return res + enddef + # script not loaded when compiling + defcompile + assert_equal('no', g:loaded) + + assert_equal('relfunc/some text', AFunc()) + assert_equal('yes', g:loaded) + unlet g:loaded + + assert_equal('from AFunc', XimportRel.someText) + XimportRel.someText = 'from script' + assert_equal('from script', XimportRel.someText) + END + v9.CheckScriptSuccess(lines) + + lines =<< trim END + vim9script + import autoload './XimportRel.vim' + echo XimportRel.NotExported() + END + v9.CheckScriptFailure(lines, 'E1049: Item not exported in script: NotExported', 3) + + lines =<< trim END + vim9script + import autoload './XimportRel.vim' + echo XimportRel.notexp + END + v9.CheckScriptFailure(lines, 'E1049: Item not exported in script: notexp', 3) + + lines =<< trim END + vim9script + import autoload './XimportRel.vim' + XimportRel.notexp = 'bad' + END + v9.CheckScriptFailure(lines, 'E1049: Item not exported in script: notexp', 3) + + lines =<< trim END + vim9script + import autoload './XimportRel.vim' + def Func() + echo XimportRel.NotExported() + enddef + Func() + END + v9.CheckScriptFailure(lines, 'E1049: Item not exported in script: NotExported', 1) + + lines =<< trim END + vim9script + import autoload './XimportRel.vim' + def Func() + echo XimportRel.notexp + enddef + Func() + END + v9.CheckScriptFailure(lines, 'E1049: Item not exported in script: notexp', 1) + + lines =<< trim END + vim9script + import autoload './XimportRel.vim' + def Func() + XimportRel.notexp = 'bad' + enddef + Func() + END + v9.CheckScriptFailure(lines, 'E1049: Item not exported in script: notexp', 1) + + # does not fail if the script wasn't loaded yet + g:loaded = 'no' + lines =<< trim END + vim9script + import autoload './XimportRel2.vim' + def Func() + echo XimportRel2.notexp + enddef + defcompile + END + v9.CheckScriptSuccess(lines) + assert_equal('no', g:loaded) + + # fails with a not loaded import + lines =<< trim END + vim9script + import autoload './XimportRel3.vim' + def Func() + XimportRel3.notexp = 'bad' + enddef + Func() + END + v9.CheckScriptFailure(lines, 'E1049: Item not exported in script: notexp', 1) + assert_equal('yes', g:loaded) + unlet g:loaded + + delete('XimportRel.vim') + delete('XimportRel2.vim') + delete('XimportRel3.vim') +enddef + func Test_import_in_diffexpr() CheckExecutable diff @@ -2379,13 +2508,13 @@ def Test_import_autoload_fails() vim9script import autoload './doesNotExist.vim' END - v9.CheckScriptFailure(lines, 'E1264:') + v9.CheckScriptSuccess(lines) lines =<< trim END vim9script import autoload '/dir/doesNotExist.vim' END - v9.CheckScriptFailure(lines, 'E1264:') + v9.CheckScriptSuccess(lines) lines =<< trim END vim9script diff --git a/src/userfunc.c b/src/userfunc.c --- a/src/userfunc.c +++ b/src/userfunc.c @@ -1884,23 +1884,33 @@ fname_trans_sid(char_u *name, char_u *fn } /* + * Concatenate the script ID and function name into "99_name". + * "buffer" must have size MAX_FUNC_NAME_LEN. + */ + void +func_name_with_sid(char_u *name, int sid, char_u *buffer) +{ + // A script-local function is stored as "99_name". + buffer[0] = K_SPECIAL; + buffer[1] = KS_EXTRA; + buffer[2] = (int)KE_SNR; + vim_snprintf((char *)buffer + 3, MAX_FUNC_NAME_LEN - 3, "%ld_%s", + (long)sid, name); +} + +/* * Find a function "name" in script "sid". */ static ufunc_T * find_func_with_sid(char_u *name, int sid) { hashitem_T *hi; - char_u buffer[200]; + char_u buffer[MAX_FUNC_NAME_LEN]; if (!SCRIPT_ID_VALID(sid)) return NULL; // not in a script - // A script-local function is stored as "99_name". - buffer[0] = K_SPECIAL; - buffer[1] = KS_EXTRA; - buffer[2] = (int)KE_SNR; - vim_snprintf((char *)buffer + 3, sizeof(buffer) - 3, "%ld_%s", - (long)sid, name); + func_name_with_sid(name, sid, buffer); hi = hash_find(&func_hashtab, buffer); if (!HASHITEM_EMPTY(hi)) return HI2UF(hi); @@ -1914,7 +1924,7 @@ find_func_with_sid(char_u *name, int sid find_func_with_prefix(char_u *name, int sid) { hashitem_T *hi; - char_u buffer[200]; + char_u buffer[MAX_FUNC_NAME_LEN]; scriptitem_T *si; if (vim_strchr(name, AUTOLOAD_CHAR) != NULL) diff --git a/src/version.c b/src/version.c --- a/src/version.c +++ b/src/version.c @@ -751,6 +751,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ /**/ + 4650, +/**/ 4649, /**/ 4648, diff --git a/src/vim.h b/src/vim.h --- a/src/vim.h +++ b/src/vim.h @@ -1571,6 +1571,9 @@ typedef UINT32_TYPEDEF UINT32_T; */ #define MAXMAPLEN 50 +// maximum length of a function name, including SID and NUL +#define MAX_FUNC_NAME_LEN 200 + // Size in bytes of the hash used in the undo file. #define UNDO_HASH_SIZE 32 diff --git a/src/vim9.h b/src/vim9.h --- a/src/vim9.h +++ b/src/vim9.h @@ -28,6 +28,8 @@ typedef enum { ISN_ECHOERR, // :echoerr with isn_arg.number items on top of stack ISN_RANGE, // compute range from isn_arg.string, push to stack ISN_SUBSTITUTE, // :s command with expression + + ISN_SOURCE, // source autoload script, isn_arg.number is the script ID ISN_INSTR, // instructions compiled from expression // get and set variables @@ -43,6 +45,7 @@ typedef enum { ISN_LOADWDICT, // push w: dict ISN_LOADTDICT, // push t: dict ISN_LOADS, // push s: variable isn_arg.loadstore + ISN_LOADEXPORT, // push exported variable isn_arg.loadstore ISN_LOADOUTER, // push variable from outer scope isn_arg.outer ISN_LOADSCRIPT, // push script-local variable isn_arg.script. ISN_LOADOPT, // push option isn_arg.string @@ -57,6 +60,7 @@ typedef enum { ISN_STOREW, // pop into window-local variable isn_arg.string ISN_STORET, // pop into tab-local variable isn_arg.string ISN_STORES, // pop into script variable isn_arg.loadstore + ISN_STOREEXPORT, // pop into exported script variable isn_arg.loadstore ISN_STOREOUTER, // pop variable into outer scope isn_arg.outer ISN_STORESCRIPT, // pop into script variable isn_arg.script ISN_STOREOPT, // pop into option isn_arg.storeopt diff --git a/src/vim9execute.c b/src/vim9execute.c --- a/src/vim9execute.c +++ b/src/vim9execute.c @@ -1510,6 +1510,13 @@ get_script_svar(scriptref_T *sref, int d emsg(_(e_script_variable_type_changed)); return NULL; } + + if (!sv->sv_export && sref->sref_sid != current_sctx.sc_sid) + { + if (dfunc != NULL) + semsg(_(e_item_not_exported_in_script_str), sv->sv_name); + return NULL; + } return sv; } @@ -2623,6 +2630,20 @@ exec_instructions(ectx_T *ectx) } break; + case ISN_SOURCE: + { + scriptitem_T *si = SCRIPT_ITEM(iptr->isn_arg.number); + + if (si->sn_state == SN_STATE_NOT_LOADED) + { + SOURCING_LNUM = iptr->isn_lnum; + if (do_source(si->sn_name, FALSE, DOSO_NONE, NULL) + == FAIL) + goto on_error; + } + } + break; + // execute :substitute with an expression case ISN_SUBSTITUTE: { @@ -2902,11 +2923,12 @@ exec_instructions(ectx_T *ectx) } break; - // load s: variable in old script + // load s: variable in old script or autoload import case ISN_LOADS: + case ISN_LOADEXPORT: { - hashtab_T *ht = &SCRIPT_VARS( - iptr->isn_arg.loadstore.ls_sid); + int sid = iptr->isn_arg.loadstore.ls_sid; + hashtab_T *ht = &SCRIPT_VARS(sid); char_u *name = iptr->isn_arg.loadstore.ls_name; dictitem_T *di = find_var_in_ht(ht, 0, name, TRUE); @@ -2918,6 +2940,25 @@ exec_instructions(ectx_T *ectx) } else { + if (iptr->isn_type == ISN_LOADEXPORT) + { + int idx = get_script_item_idx(sid, name, 0, + NULL, NULL); + svar_T *sv; + + if (idx >= 0) + { + sv = ((svar_T *)SCRIPT_ITEM(sid) + ->sn_var_vals.ga_data) + idx; + if (!sv->sv_export) + { + SOURCING_LNUM = iptr->isn_lnum; + semsg(_(e_item_not_exported_in_script_str), + name); + goto on_error; + } + } + } if (GA_GROW_FAILS(&ectx->ec_stack, 1)) goto theend; copy_tv(&di->di_tv, STACK_TV_BOT(0)); @@ -3039,20 +3080,48 @@ exec_instructions(ectx_T *ectx) *tv = *STACK_TV_BOT(0); break; - // store s: variable in old script + // store s: variable in old script or autoload import case ISN_STORES: + case ISN_STOREEXPORT: { - hashtab_T *ht = &SCRIPT_VARS( - iptr->isn_arg.loadstore.ls_sid); + int sid = iptr->isn_arg.loadstore.ls_sid; + hashtab_T *ht = &SCRIPT_VARS(sid); char_u *name = iptr->isn_arg.loadstore.ls_name; - dictitem_T *di = find_var_in_ht(ht, 0, name + 2, TRUE); + dictitem_T *di = find_var_in_ht(ht, 0, + iptr->isn_type == ISN_STORES + ? name + 2 : name, TRUE); --ectx->ec_stack.ga_len; + SOURCING_LNUM = iptr->isn_lnum; if (di == NULL) + { + if (iptr->isn_type == ISN_STOREEXPORT) + { + semsg(_(e_undefined_variable_str), name); + goto on_error; + } store_var(name, STACK_TV_BOT(0)); + } else { - SOURCING_LNUM = iptr->isn_lnum; + if (iptr->isn_type == ISN_STOREEXPORT) + { + int idx = get_script_item_idx(sid, name, 0, + NULL, NULL); + + if (idx >= 0) + { + svar_T *sv = ((svar_T *)SCRIPT_ITEM(sid) + ->sn_var_vals.ga_data) + idx; + + if (!sv->sv_export) + { + semsg(_(e_item_not_exported_in_script_str), + name); + goto on_error; + } + } + } if (var_check_permission(di, name) == FAIL) { clear_tv(STACK_TV_BOT(0)); @@ -5409,11 +5478,15 @@ list_instructions(char *pfx, isn_T *inst #endif break; case ISN_INSTR: + smsg("%s%4d INSTR", pfx, current); + list_instructions(" ", iptr->isn_arg.instr, INT_MAX, NULL); + msg(" -------------"); + break; + case ISN_SOURCE: { - smsg("%s%4d INSTR", pfx, current); - list_instructions(" ", iptr->isn_arg.instr, - INT_MAX, NULL); - msg(" -------------"); + scriptitem_T *si = SCRIPT_ITEM(iptr->isn_arg.number); + + smsg("%s%4d SOURCE %s", pfx, current, si->sn_name); } break; case ISN_SUBSTITUTE: @@ -5500,12 +5573,15 @@ list_instructions(char *pfx, isn_T *inst } break; case ISN_LOADS: + case ISN_LOADEXPORT: { scriptitem_T *si = SCRIPT_ITEM( iptr->isn_arg.loadstore.ls_sid); - smsg("%s%4d LOADS s:%s from %s", pfx, current, - iptr->isn_arg.loadstore.ls_name, si->sn_name); + smsg("%s%4d %s s:%s from %s", pfx, current, + iptr->isn_type == ISN_LOADS ? "LOADS" + : "LOADEXPORT", + iptr->isn_arg.loadstore.ls_name, si->sn_name); } break; case ISN_LOADAUTO: @@ -5586,11 +5662,14 @@ list_instructions(char *pfx, isn_T *inst smsg("%s%4d STORET %s", pfx, current, iptr->isn_arg.string); break; case ISN_STORES: + case ISN_STOREEXPORT: { scriptitem_T *si = SCRIPT_ITEM( iptr->isn_arg.loadstore.ls_sid); - smsg("%s%4d STORES %s in %s", pfx, current, + smsg("%s%4d %s %s in %s", pfx, current, + iptr->isn_type == ISN_STORES + ? "STORES" : "STOREEXPORT", iptr->isn_arg.loadstore.ls_name, si->sn_name); } break; diff --git a/src/vim9expr.c b/src/vim9expr.c --- a/src/vim9expr.c +++ b/src/vim9expr.c @@ -313,6 +313,27 @@ compile_load_scriptvar( vim_free(auto_name); done = TRUE; } + else if (si->sn_import_autoload && si->sn_state == SN_STATE_NOT_LOADED) + { + // "import autoload './dir/script.vim'" - load script first + res = generate_SOURCE(cctx, import->imp_sid); + if (res == OK) + { + // If a '(' follows it must be a function. Otherwise we don't + // know, it can be "script.Func". + if (cc == '(' || paren_follows_after_expr) + { + char_u sid_name[MAX_FUNC_NAME_LEN]; + + func_name_with_sid(exp_name, import->imp_sid, sid_name); + res = generate_PUSHFUNC(cctx, sid_name, &t_func_any); + } + else + res = generate_OLDSCRIPT(cctx, ISN_LOADEXPORT, exp_name, + import->imp_sid, &t_any); + } + done = TRUE; + } else { idx = find_exported(import->imp_sid, exp_name, &ufunc, &type, diff --git a/src/vim9instr.c b/src/vim9instr.c --- a/src/vim9instr.c +++ b/src/vim9instr.c @@ -1066,7 +1066,7 @@ generate_OLDSCRIPT( isn_T *isn; RETURN_OK_IF_SKIP(cctx); - if (isn_type == ISN_LOADS) + if (isn_type == ISN_LOADS || isn_type == ISN_LOADEXPORT) isn = generate_instr_type(cctx, isn_type, type); else isn = generate_instr_drop(cctx, isn_type, 1); @@ -1728,6 +1728,21 @@ generate_MULT_EXPR(cctx_T *cctx, isntype } /* + * Generate an ISN_SOURCE instruction. + */ + int +generate_SOURCE(cctx_T *cctx, int sid) +{ + isn_T *isn; + + if ((isn = generate_instr(cctx, ISN_SOURCE)) == NULL) + return FAIL; + isn->isn_arg.number = sid; + + return OK; +} + +/* * Generate an ISN_PUT instruction. */ int @@ -1913,9 +1928,22 @@ generate_store_var( return generate_STORE(cctx, ISN_STOREV, vimvaridx, NULL); case dest_script: if (scriptvar_idx < 0) + { + isntype_T isn_type = ISN_STORES; + + if (SCRIPT_ID_VALID(scriptvar_sid) + && SCRIPT_ITEM(scriptvar_sid)->sn_import_autoload) + { + // "import autoload './dir/script.vim'" - load script first + if (generate_SOURCE(cctx, scriptvar_sid) == FAIL) + return FAIL; + isn_type = ISN_STOREEXPORT; + } + // "s:" may be included in the name. - return generate_OLDSCRIPT(cctx, ISN_STORES, name, - scriptvar_sid, type); + return generate_OLDSCRIPT(cctx, isn_type, name, + scriptvar_sid, type); + } return generate_VIM9SCRIPT(cctx, ISN_STORESCRIPT, scriptvar_sid, scriptvar_idx, type); case dest_local: @@ -2062,7 +2090,9 @@ delete_instr(isn_T *isn) break; case ISN_LOADS: + case ISN_LOADEXPORT: case ISN_STORES: + case ISN_STOREEXPORT: vim_free(isn->isn_arg.loadstore.ls_name); break; @@ -2089,7 +2119,7 @@ delete_instr(isn_T *isn) if (isn->isn_arg.funcref.fr_func_name == NULL) { dfunc_T *dfunc = ((dfunc_T *)def_functions.ga_data) - + isn->isn_arg.funcref.fr_dfunc_idx; + + isn->isn_arg.funcref.fr_dfunc_idx; ufunc_T *ufunc = dfunc->df_ufunc; if (ufunc != NULL && func_name_refcount(ufunc->uf_name)) @@ -2109,10 +2139,10 @@ delete_instr(isn_T *isn) case ISN_DCALL: { dfunc_T *dfunc = ((dfunc_T *)def_functions.ga_data) - + isn->isn_arg.dfunc.cdf_idx; + + isn->isn_arg.dfunc.cdf_idx; if (dfunc->df_ufunc != NULL - && func_name_refcount(dfunc->df_ufunc->uf_name)) + && func_name_refcount(dfunc->df_ufunc->uf_name)) func_ptr_unref(dfunc->df_ufunc); } break; @@ -2140,7 +2170,7 @@ delete_instr(isn_T *isn) case ISN_CMDMOD: vim_regfree(isn->isn_arg.cmdmod.cf_cmdmod - ->cmod_filter_regmatch.regprog); + ->cmod_filter_regmatch.regprog); vim_free(isn->isn_arg.cmdmod.cf_cmdmod); break; @@ -2243,21 +2273,22 @@ delete_instr(isn_T *isn) case ISN_STORE: case ISN_STOREINDEX: case ISN_STORENR: - case ISN_STOREOUTER: - case ISN_STORERANGE: - case ISN_STOREREG: - case ISN_STOREV: - case ISN_STRINDEX: - case ISN_STRSLICE: - case ISN_THROW: - case ISN_TRYCONT: - case ISN_UNLETINDEX: - case ISN_UNLETRANGE: - case ISN_UNPACK: - case ISN_USEDICT: - // nothing allocated - break; - } + case ISN_SOURCE: + case ISN_STOREOUTER: + case ISN_STORERANGE: + case ISN_STOREREG: + case ISN_STOREV: + case ISN_STRINDEX: + case ISN_STRSLICE: + case ISN_THROW: + case ISN_TRYCONT: + case ISN_UNLETINDEX: + case ISN_UNLETRANGE: + case ISN_UNPACK: + case ISN_USEDICT: + // nothing allocated + break; +} } void diff --git a/src/vim9script.c b/src/vim9script.c --- a/src/vim9script.c +++ b/src/vim9script.c @@ -384,6 +384,38 @@ mark_imports_for_reload(int sid) } /* + * Part of "import" that handles a relative or absolute file name/ + * Returns OK or FAIL. + */ + static int +handle_import_fname(char_u *fname, int is_autoload, int *sid) +{ + if (is_autoload) + { + scriptitem_T *si; + + *sid = find_script_by_name(fname); + if (*sid < 0) + { + int error = OK; + + // script does not exist yet, create a new scriptitem + *sid = get_new_scriptitem_for_fname(&error, fname); + if (error == FAIL) + return FAIL; + } + + si = SCRIPT_ITEM(*sid); + si->sn_import_autoload = TRUE; + + // with testing override: load autoload script right away + if (!override_autoload || si->sn_state != SN_STATE_NOT_LOADED) + return OK; + } + return do_source(fname, FALSE, DOSO_NONE, sid); +} + +/* * Handle an ":import" command and add the resulting imported_T to "gap", when * not NULL, or script "import_sid" sn_imports. * "cctx" is NULL at the script level. @@ -442,25 +474,18 @@ handle_import( char_u *tail = gettail(si->sn_name); char_u *from_name; - if (is_autoload) - res = FAIL; - else - { + // Relative to current script: "./name.vim", "../../name.vim". + len = STRLEN(si->sn_name) - STRLEN(tail) + STRLEN(tv.vval.v_string) + 2; + from_name = alloc((int)len); + if (from_name == NULL) + goto erret; + vim_strncpy(from_name, si->sn_name, tail - si->sn_name); + add_pathsep(from_name); + STRCAT(from_name, tv.vval.v_string); + simplify_filename(from_name); - // Relative to current script: "./name.vim", "../../name.vim". - len = STRLEN(si->sn_name) - STRLEN(tail) - + STRLEN(tv.vval.v_string) + 2; - from_name = alloc((int)len); - if (from_name == NULL) - goto erret; - vim_strncpy(from_name, si->sn_name, tail - si->sn_name); - add_pathsep(from_name); - STRCAT(from_name, tv.vval.v_string); - simplify_filename(from_name); - - res = do_source(from_name, FALSE, DOSO_NONE, &sid); - vim_free(from_name); - } + res = handle_import_fname(from_name, is_autoload, &sid); + vim_free(from_name); } else if (mch_isFullName(tv.vval.v_string) #ifdef BACKSLASH_IN_FILENAME @@ -471,10 +496,7 @@ handle_import( ) { // Absolute path: "/tmp/name.vim" - if (is_autoload) - res = FAIL; - else - res = do_source(tv.vval.v_string, FALSE, DOSO_NONE, &sid); + res = handle_import_fname(tv.vval.v_string, is_autoload, &sid); } else if (is_autoload) { @@ -677,6 +699,12 @@ find_exported( svar_T *sv; scriptitem_T *script = SCRIPT_ITEM(sid); + if (script->sn_import_autoload && script->sn_state == SN_STATE_NOT_LOADED) + { + if (do_source(script->sn_name, FALSE, DOSO_NONE, NULL) == FAIL) + return -1; + } + // Find name in "script". idx = get_script_item_idx(sid, name, 0, cctx, cstack); if (idx >= 0)