To: vim_dev@googlegroups.com Subject: Patch 8.2.4650 Fcc: outbox From: Bram Moolenaar Mime-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ------------ Patch 8.2.4650 Problem: "import autoload" only works with using 'runtimepath'. Solution: Also support a relative and absolute file name. Files: runtime/doc/vim9.txt, src/structs.h, src/scriptfile.c, src/proto/scriptfile.pro, src/vim9script.c, src/vim9expr.c, src/vim9.h, src/vim9execute.c, src/vim9instr.c, src/proto/vim9instr.pro, src/vim.h, src/userfunc.c, src/proto/userfunc.pro, src/testdir/test_vim9_import.vim, src/testdir/test_vim9_disassemble.vim *** ../vim-8.2.4649/runtime/doc/vim9.txt 2022-03-08 13:18:10.805020790 +0000 --- runtime/doc/vim9.txt 2022-03-30 19:57:51.472168649 +0100 *************** *** 1529,1545 **** echo that .name # Error! < *:import-cycle* ! The `import` commands are executed when encountered. If that script (directly ! or indirectly) imports the current script, then items defined after the ! `import` won't be processed yet. Therefore cyclic imports can exist, but may ! result in undefined items. Importing an autoload script ~ *vim9-autoload* For optimal startup speed, loading scripts should be postponed until they are actually needed. Using the autoload mechanism is recommended: ! 1. In the plugin define user commands, functions and/or mappings that refer to items imported from an autoload script. > import autoload 'for/search.vim' --- 1721,1739 ---- echo that .name # Error! < *:import-cycle* ! The `import` commands are executed when encountered. If script A imports ! script B, and B (directly or indirectly) imports A, this will be skipped over. ! At this point items in A after "import B" will not have been processed and ! defined yet. Therefore cyclic imports can exist and not result in an error ! directly, but may result in an error for items in A after "import B" not being ! defined. This does not apply to autoload imports, see the next section. Importing an autoload script ~ *vim9-autoload* For optimal startup speed, loading scripts should be postponed until they are actually needed. Using the autoload mechanism is recommended: ! *E1264* 1. In the plugin define user commands, functions and/or mappings that refer to items imported from an autoload script. > import autoload 'for/search.vim' *************** *** 1551,1557 **** The "autoload" argument to `:import` means that the script is not loaded until one of the items is actually used. The script will be found under the "autoload" directory in 'runtimepath' instead of the "import" ! directory. 2. In the autoload script put the bulk of the code. > vim9script --- 1745,1752 ---- The "autoload" argument to `:import` means that the script is not loaded until one of the items is actually used. The script will be found under the "autoload" directory in 'runtimepath' instead of the "import" ! directory. Alternatively a relative or absolute name can be used, see ! below. 2. In the autoload script put the bulk of the code. > vim9script *************** *** 1565,1575 **** prefix is obtained from the file name, as you would to manually in a legacy autoload script. Thus the exported function can be found with "for#search#Stuff", but you would normally use `import autoload` and not ! use the prefix. You can split up the functionality and import other scripts from the autoload script as you like. This way you can share code between plugins. For defining a mapping that uses the imported autoload script the special key || is useful. It allows for a command in a mapping to use the script context of where the mapping was defined. --- 1760,1779 ---- prefix is obtained from the file name, as you would to manually in a legacy autoload script. Thus the exported function can be found with "for#search#Stuff", but you would normally use `import autoload` and not ! use the prefix (which has the side effect of loading the autoload script ! when compiling a function that encounters this name). You can split up the functionality and import other scripts from the autoload script as you like. This way you can share code between plugins. + Searching for the autoload script in all entries in 'runtimepath' can be a bit + slow. If the plugin knows where the script is located, quite often a relative + path can be used. This avoids the search and should be quite a bit faster. + Another advantage is that the script name does not need to be unique. An + absolute path is also possible. Examples: > + import autoload '../lib/implement.vim' + import autoload MyScriptsDir .. '/lib/implement.vim' + For defining a mapping that uses the imported autoload script the special key || is useful. It allows for a command in a mapping to use the script context of where the mapping was defined. *************** *** 1577,1583 **** When compiling a `:def` function and a function in an autoload script is encountered, the script is not loaded until the `:def` function is called. This also means you get any errors only at runtime, since the argument and ! return types are not known yet. For testing the |test_override()| function can be used to have the `import autoload` load the script right away, so that the items and types can --- 1781,1797 ---- When compiling a `:def` function and a function in an autoload script is encountered, the script is not loaded until the `:def` function is called. This also means you get any errors only at runtime, since the argument and ! return types are not known yet. If you would use the name with '#' characters ! then the autoload script IS loaded. ! ! Be careful to not refer to an item in an autoload script that does trigger ! loading it unintentionally. For example, when setting an option that takes a ! function name, make sure to use a string, not a function reference: > ! import autoload 'qftf.vim' ! &quickfixtextfunc = 'qftf.Func' # autoload script NOT loaded ! &quickfixtextfunc = qftf.Func # autoload script IS loaded ! On the other hand, it can be useful to load the script early, at a time when ! any errors should be given. For testing the |test_override()| function can be used to have the `import autoload` load the script right away, so that the items and types can *** ../vim-8.2.4649/src/structs.h 2022-03-30 10:14:41.485657271 +0100 --- src/structs.h 2022-03-30 14:21:51.012324633 +0100 *************** *** 1833,1839 **** */ typedef struct { ! char_u *sn_name; int sn_script_seq; // latest sctx_T sc_seq value // "sn_vars" stores the s: variables currently valid. When leaving a block --- 1833,1839 ---- */ typedef struct { ! 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,1872 **** 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#" char_u *sn_autoload_prefix; # ifdef FEAT_PROFILE int sn_prof_on; // TRUE when script is/was profiled int sn_pr_force; // forceit: profile functions in this script --- 1864,1875 ---- char_u *sn_save_cpo; // 'cpo' value when :vim9script found char sn_is_vimrc; // .vimrc file, do not restore 'cpo' ! // 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 *** ../vim-8.2.4649/src/scriptfile.c 2022-03-29 19:52:08.787653549 +0100 --- src/scriptfile.c 2022-03-30 14:55:27.953493854 +0100 *************** *** 251,257 **** * Find an already loaded script "name". * If found returns its script ID. If not found returns -1. */ ! static int find_script_by_name(char_u *name) { int sid; --- 251,257 ---- * Find an already loaded script "name". * If found returns its script ID. If not found returns -1. */ ! int find_script_by_name(char_u *name) { int sid; *************** *** 320,325 **** --- 320,340 ---- 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,345 **** 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; ! } ! } *ret_sid = sid; } #endif --- 344,351 ---- sid = find_script_by_name(fname); if (sid < 0) // script does not exist yet, create a new scriptitem ! sid = get_new_scriptitem_for_fname(&error, fname); *ret_sid = sid; } #endif *** ../vim-8.2.4649/src/proto/scriptfile.pro 2022-03-21 19:45:13.200420997 +0000 --- src/proto/scriptfile.pro 2022-03-30 14:55:31.673487843 +0100 *************** *** 6,11 **** --- 6,13 ---- 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); *** ../vim-8.2.4649/src/vim9script.c 2022-03-28 15:22:31.490443719 +0100 --- src/vim9script.c 2022-03-30 15:02:11.104833760 +0100 *************** *** 384,389 **** --- 384,421 ---- } /* + * 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,466 **** 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); ! res = do_source(from_name, FALSE, DOSO_NONE, &sid); ! vim_free(from_name); ! } } else if (mch_isFullName(tv.vval.v_string) #ifdef BACKSLASH_IN_FILENAME --- 474,491 ---- char_u *tail = gettail(si->sn_name); char_u *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 = 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,480 **** ) { // Absolute path: "/tmp/name.vim" ! if (is_autoload) ! res = FAIL; ! else ! res = do_source(tv.vval.v_string, FALSE, DOSO_NONE, &sid); } else if (is_autoload) { --- 496,502 ---- ) { // Absolute path: "/tmp/name.vim" ! res = handle_import_fname(tv.vval.v_string, is_autoload, &sid); } else if (is_autoload) { *************** *** 677,682 **** --- 699,710 ---- 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) *** ../vim-8.2.4649/src/vim9expr.c 2022-03-27 16:29:49.880153368 +0100 --- src/vim9expr.c 2022-03-30 16:49:42.100316230 +0100 *************** *** 313,318 **** --- 313,339 ---- 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, *** ../vim-8.2.4649/src/vim9.h 2022-03-27 20:04:16.029188564 +0100 --- src/vim9.h 2022-03-30 17:10:56.817448503 +0100 *************** *** 28,33 **** --- 28,35 ---- 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,48 **** --- 45,51 ---- 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,62 **** --- 60,66 ---- 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 *** ../vim-8.2.4649/src/vim9execute.c 2022-03-28 18:16:43.619673423 +0100 --- src/vim9execute.c 2022-03-30 20:27:54.479338636 +0100 *************** *** 1510,1515 **** --- 1510,1522 ---- 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,2628 **** --- 2630,2649 ---- } 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,2912 **** } break; ! // load s: variable in old script case ISN_LOADS: { ! hashtab_T *ht = &SCRIPT_VARS( ! iptr->isn_arg.loadstore.ls_sid); char_u *name = iptr->isn_arg.loadstore.ls_name; dictitem_T *di = find_var_in_ht(ht, 0, name, TRUE); --- 2923,2934 ---- } break; ! // load s: variable in old script or autoload import case ISN_LOADS: + case ISN_LOADEXPORT: { ! 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,2923 **** --- 2940,2964 ---- } 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,3058 **** *tv = *STACK_TV_BOT(0); break; ! // store s: variable in old script case ISN_STORES: { ! hashtab_T *ht = &SCRIPT_VARS( ! iptr->isn_arg.loadstore.ls_sid); char_u *name = iptr->isn_arg.loadstore.ls_name; ! dictitem_T *di = find_var_in_ht(ht, 0, name + 2, TRUE); --ectx->ec_stack.ga_len; if (di == NULL) store_var(name, STACK_TV_BOT(0)); else { ! SOURCING_LNUM = iptr->isn_lnum; if (var_check_permission(di, name) == FAIL) { clear_tv(STACK_TV_BOT(0)); --- 3080,3127 ---- *tv = *STACK_TV_BOT(0); break; ! // store s: variable in old script or autoload import case ISN_STORES: + case ISN_STOREEXPORT: { ! 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, ! 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 { ! 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,5419 **** #endif break; case ISN_INSTR: { ! smsg("%s%4d INSTR", pfx, current); ! list_instructions(" ", iptr->isn_arg.instr, ! INT_MAX, NULL); ! msg(" -------------"); } break; case ISN_SUBSTITUTE: --- 5478,5492 ---- #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: { ! scriptitem_T *si = SCRIPT_ITEM(iptr->isn_arg.number); ! ! smsg("%s%4d SOURCE %s", pfx, current, si->sn_name); } break; case ISN_SUBSTITUTE: *************** *** 5500,5511 **** } break; case ISN_LOADS: { 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); } break; case ISN_LOADAUTO: --- 5573,5587 ---- } break; case ISN_LOADS: + case ISN_LOADEXPORT: { scriptitem_T *si = SCRIPT_ITEM( iptr->isn_arg.loadstore.ls_sid); ! 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,5596 **** smsg("%s%4d STORET %s", pfx, current, iptr->isn_arg.string); break; case ISN_STORES: { scriptitem_T *si = SCRIPT_ITEM( iptr->isn_arg.loadstore.ls_sid); ! smsg("%s%4d STORES %s in %s", pfx, current, iptr->isn_arg.loadstore.ls_name, si->sn_name); } break; --- 5662,5675 ---- 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 %s %s in %s", pfx, current, ! iptr->isn_type == ISN_STORES ! ? "STORES" : "STOREEXPORT", iptr->isn_arg.loadstore.ls_name, si->sn_name); } break; *** ../vim-8.2.4649/src/vim9instr.c 2022-03-27 16:29:49.876153380 +0100 --- src/vim9instr.c 2022-03-30 20:40:15.387219798 +0100 *************** *** 1066,1072 **** isn_T *isn; RETURN_OK_IF_SKIP(cctx); ! if (isn_type == ISN_LOADS) isn = generate_instr_type(cctx, isn_type, type); else isn = generate_instr_drop(cctx, isn_type, 1); --- 1066,1072 ---- isn_T *isn; RETURN_OK_IF_SKIP(cctx); ! 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,1733 **** --- 1728,1748 ---- } /* + * 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,1921 **** return generate_STORE(cctx, ISN_STOREV, vimvaridx, NULL); case dest_script: if (scriptvar_idx < 0) // "s:" may be included in the name. ! return generate_OLDSCRIPT(cctx, ISN_STORES, name, ! scriptvar_sid, type); return generate_VIM9SCRIPT(cctx, ISN_STORESCRIPT, scriptvar_sid, scriptvar_idx, type); case dest_local: --- 1928,1949 ---- 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_type, name, ! scriptvar_sid, type); ! } return generate_VIM9SCRIPT(cctx, ISN_STORESCRIPT, scriptvar_sid, scriptvar_idx, type); case dest_local: *************** *** 2062,2068 **** --- 2090,2098 ---- break; case ISN_LOADS: + case ISN_LOADEXPORT: case ISN_STORES: + case ISN_STOREEXPORT: vim_free(isn->isn_arg.loadstore.ls_name); break; *************** *** 2089,2095 **** 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; ufunc_T *ufunc = dfunc->df_ufunc; if (ufunc != NULL && func_name_refcount(ufunc->uf_name)) --- 2119,2125 ---- 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; ufunc_T *ufunc = dfunc->df_ufunc; if (ufunc != NULL && func_name_refcount(ufunc->uf_name)) *************** *** 2109,2118 **** case ISN_DCALL: { dfunc_T *dfunc = ((dfunc_T *)def_functions.ga_data) ! + isn->isn_arg.dfunc.cdf_idx; if (dfunc->df_ufunc != NULL ! && func_name_refcount(dfunc->df_ufunc->uf_name)) func_ptr_unref(dfunc->df_ufunc); } break; --- 2139,2148 ---- case ISN_DCALL: { dfunc_T *dfunc = ((dfunc_T *)def_functions.ga_data) ! + isn->isn_arg.dfunc.cdf_idx; if (dfunc->df_ufunc != NULL ! && func_name_refcount(dfunc->df_ufunc->uf_name)) func_ptr_unref(dfunc->df_ufunc); } break; *************** *** 2140,2146 **** case ISN_CMDMOD: vim_regfree(isn->isn_arg.cmdmod.cf_cmdmod ! ->cmod_filter_regmatch.regprog); vim_free(isn->isn_arg.cmdmod.cf_cmdmod); break; --- 2170,2176 ---- case ISN_CMDMOD: vim_regfree(isn->isn_arg.cmdmod.cf_cmdmod ! ->cmod_filter_regmatch.regprog); vim_free(isn->isn_arg.cmdmod.cf_cmdmod); break; *************** *** 2243,2263 **** 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; ! } } void --- 2273,2294 ---- case ISN_STORE: case ISN_STOREINDEX: case ISN_STORENR: ! 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 *** ../vim-8.2.4649/src/proto/vim9instr.pro 2022-03-27 16:29:49.876153380 +0100 --- src/proto/vim9instr.pro 2022-03-30 15:26:07.158733162 +0100 *************** *** 54,59 **** --- 54,60 ---- 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); *** ../vim-8.2.4649/src/vim.h 2022-03-28 15:22:31.490443719 +0100 --- src/vim.h 2022-03-30 15:36:18.057768667 +0100 *************** *** 1571,1576 **** --- 1571,1579 ---- */ #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 *** ../vim-8.2.4649/src/userfunc.c 2022-03-17 16:30:00.174908142 +0000 --- src/userfunc.c 2022-03-30 15:58:23.858836587 +0100 *************** *** 1884,1906 **** } /* * 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]; 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); hi = hash_find(&func_hashtab, buffer); if (!HASHITEM_EMPTY(hi)) return HI2UF(hi); --- 1884,1916 ---- } /* + * 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[MAX_FUNC_NAME_LEN]; if (!SCRIPT_ID_VALID(sid)) return NULL; // not in a script ! func_name_with_sid(name, sid, buffer); hi = hash_find(&func_hashtab, buffer); if (!HASHITEM_EMPTY(hi)) return HI2UF(hi); *************** *** 1914,1920 **** find_func_with_prefix(char_u *name, int sid) { hashitem_T *hi; ! char_u buffer[200]; scriptitem_T *si; if (vim_strchr(name, AUTOLOAD_CHAR) != NULL) --- 1924,1930 ---- find_func_with_prefix(char_u *name, int sid) { hashitem_T *hi; ! char_u buffer[MAX_FUNC_NAME_LEN]; scriptitem_T *si; if (vim_strchr(name, AUTOLOAD_CHAR) != NULL) *** ../vim-8.2.4649/src/proto/userfunc.pro 2022-02-21 13:13:44.919693176 +0000 --- src/proto/userfunc.pro 2022-03-30 15:41:26.061275045 +0100 *************** *** 8,13 **** --- 8,14 ---- 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); *** ../vim-8.2.4649/src/testdir/test_vim9_import.vim 2022-03-21 20:40:32.408367357 +0000 --- src/testdir/test_vim9_import.vim 2022-03-30 20:57:56.315691134 +0100 *************** *** 840,845 **** --- 840,974 ---- &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,2391 **** vim9script import autoload './doesNotExist.vim' END ! v9.CheckScriptFailure(lines, 'E1264:') lines =<< trim END vim9script import autoload '/dir/doesNotExist.vim' END ! v9.CheckScriptFailure(lines, 'E1264:') lines =<< trim END vim9script --- 2508,2520 ---- vim9script import autoload './doesNotExist.vim' END ! v9.CheckScriptSuccess(lines) lines =<< trim END vim9script import autoload '/dir/doesNotExist.vim' END ! v9.CheckScriptSuccess(lines) lines =<< trim END vim9script *** ../vim-8.2.4649/src/testdir/test_vim9_disassemble.vim 2022-03-27 16:29:49.880153368 +0100 --- src/testdir/test_vim9_disassemble.vim 2022-03-30 21:07:59.595912515 +0100 *************** *** 318,323 **** --- 318,363 ---- &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 *** ../vim-8.2.4649/src/version.c 2022-03-30 10:57:36.739346189 +0100 --- src/version.c 2022-03-30 14:20:17.028426263 +0100 *************** *** 752,753 **** --- 752,755 ---- { /* Add new patch number below this line */ + /**/ + 4650, /**/ -- Two cows are standing together in a field. One asks the other: "So what do you think about this Mad Cow Disease?" The other replies: "That doesn't concern me. I'm a helicopter." /// Bram Moolenaar -- Bram@Moolenaar.net -- http://www.Moolenaar.net \\\ /// \\\ \\\ sponsor Vim, vote for features -- http://www.Vim.org/sponsor/ /// \\\ help me help AIDS victims -- http://ICCF-Holland.org ///