# HG changeset patch # User Bram Moolenaar # Date 1609073104 -3600 # Node ID 17a0e32eefd4622a86ccae676f7f26a14054b689 # Parent b5274c21249f69604ea30824c82756571cf0b555 patch 8.2.2225: Vim9: error when using :import in legacy script twice Commit: https://github.com/vim/vim/commit/a6294955308fac1d14b9a8481e7f8581146c706c Author: Bram Moolenaar Date: Sun Dec 27 13:39:50 2020 +0100 patch 8.2.2225: Vim9: error when using :import in legacy script twice Problem: Vim9: error when using :import in legacy script twice. Solution: Make it possible to redefine an import when reloading. diff --git a/src/evalvars.c b/src/evalvars.c --- a/src/evalvars.c +++ b/src/evalvars.c @@ -2531,7 +2531,7 @@ eval_variable( rettv->vval.v_string = vim_strsave(import->imp_funcname); } } - else if (import->imp_all) + else if (import->imp_flags & IMP_FLAGS_STAR) { emsg("Sorry, 'import * as X' not implemented yet"); ret = FAIL; diff --git a/src/proto/vim9script.pro b/src/proto/vim9script.pro --- a/src/proto/vim9script.pro +++ b/src/proto/vim9script.pro @@ -4,6 +4,7 @@ void ex_vim9script(exarg_T *eap); int not_in_vim9(exarg_T *eap); void ex_export(exarg_T *eap); void free_imports_and_script_vars(int sid); +void mark_imports_for_reload(int sid); void ex_import(exarg_T *eap); int find_exported(int sid, char_u *name, ufunc_T **ufunc, type_T **type, cctx_T *cctx); char_u *handle_import(char_u *arg_start, garray_T *gap, int import_sid, evalarg_T *evalarg, void *cctx); diff --git a/src/structs.h b/src/structs.h --- a/src/structs.h +++ b/src/structs.h @@ -1778,17 +1778,19 @@ typedef struct { char_u *imp_name; // name imported as (allocated) int imp_sid; // script ID of "from" - // for "import * as Name", "imp_name" is "Name" - int imp_all; - - // for variable + int imp_flags; // IMP_FLAGS_ values + + // for a variable type_T *imp_type; int imp_var_vals_idx; // index in sn_var_vals of "from" - // for function + // for a function char_u *imp_funcname; // user func name (NOT allocated) } imported_T; +#define IMP_FLAGS_STAR 1 // using "import * as Name" +#define IMP_FLAGS_RELOAD 2 // script reloaded, OK to redefine + /* * Info about an already sourced scripts. */ 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 @@ -899,6 +899,16 @@ def Test_vim9_import_export() writefile(import_star_as_lines_dot_space, 'Ximport.vim') assert_fails('source Ximport.vim', 'E1074:', '', 1, 'Func') + var import_star_as_duplicated =<< trim END + vim9script + import * as Export from './Xexport.vim' + var some = 'other' + import * as Export from './Xexport.vim' + defcompile + END + writefile(import_star_as_duplicated, 'Ximport.vim') + assert_fails('source Ximport.vim', 'E1073:', '', 4, 'Ximport.vim') + var import_star_as_lines_missing_name =<< trim END vim9script import * as Export from './Xexport.vim' @@ -1160,9 +1170,15 @@ enddef def Test_vim9script_reload_noclear() var lines =<< trim END + vim9script + export var exported = 'thexport' + END + writefile(lines, 'XExportReload') + lines =<< trim END vim9script noclear g:loadCount += 1 var s:reloaded = 'init' + import exported from './XExportReload' def Again(): string return 'again' @@ -1174,7 +1190,7 @@ def Test_vim9script_reload_noclear() var s:notReloaded = 'yes' s:reloaded = 'first' def g:Values(): list - return [s:reloaded, s:notReloaded, Again(), Once()] + return [s:reloaded, s:notReloaded, Again(), Once(), exported] enddef def Once(): string @@ -1185,15 +1201,16 @@ def Test_vim9script_reload_noclear() g:loadCount = 0 source XReloaded assert_equal(1, g:loadCount) - assert_equal(['first', 'yes', 'again', 'once'], g:Values()) + assert_equal(['first', 'yes', 'again', 'once', 'thexport'], g:Values()) source XReloaded assert_equal(2, g:loadCount) - assert_equal(['init', 'yes', 'again', 'once'], g:Values()) + assert_equal(['init', 'yes', 'again', 'once', 'thexport'], g:Values()) source XReloaded assert_equal(3, g:loadCount) - assert_equal(['init', 'yes', 'again', 'once'], g:Values()) + assert_equal(['init', 'yes', 'again', 'once', 'thexport'], g:Values()) delete('Xreloaded') + delete('XExportReload') delfunc g:Values unlet g:loadCount enddef @@ -2762,6 +2779,17 @@ def Test_forward_declaration() enddef def Test_source_vim9_from_legacy() + var vim9_lines =<< trim END + vim9script + var local = 'local' + g:global = 'global' + export var exported = 'exported' + export def GetText(): string + return 'text' + enddef + END + writefile(vim9_lines, 'Xvim9_script.vim') + var legacy_lines =<< trim END source Xvim9_script.vim @@ -2783,19 +2811,7 @@ def Test_source_vim9_from_legacy() END writefile(legacy_lines, 'Xlegacy_script.vim') - var vim9_lines =<< trim END - vim9script - var local = 'local' - g:global = 'global' - export var exported = 'exported' - export def GetText(): string - return 'text' - enddef - END - writefile(vim9_lines, 'Xvim9_script.vim') - source Xlegacy_script.vim - assert_equal('global', g:global) unlet g:global 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 */ /**/ + 2225, +/**/ 2224, /**/ 2223, diff --git a/src/vim9compile.c b/src/vim9compile.c --- a/src/vim9compile.c +++ b/src/vim9compile.c @@ -2416,7 +2416,7 @@ compile_load_scriptvar( import = find_imported(name, 0, cctx); if (import != NULL) { - if (import->imp_all) + if (import->imp_flags & IMP_FLAGS_STAR) { char_u *p = skipwhite(*end); char_u *exp_name; diff --git a/src/vim9script.c b/src/vim9script.c --- a/src/vim9script.c +++ b/src/vim9script.c @@ -174,6 +174,24 @@ free_imports_and_script_vars(int sid) } /* + * Mark all imports as possible to redefine. Used when a script is loaded + * again but not cleared. + */ + void +mark_imports_for_reload(int sid) +{ + scriptitem_T *si = SCRIPT_ITEM(sid); + int idx; + + for (idx = 0; idx < si->sn_imports.ga_len; ++idx) + { + imported_T *imp = ((imported_T *)si->sn_imports.ga_data) + idx; + + imp->imp_flags |= IMP_FLAGS_RELOAD; + } +} + +/* * ":import Item from 'filename'" * ":import Item as Alias from 'filename'" * ":import {Item} from 'filename'". @@ -459,15 +477,29 @@ handle_import( if (*arg_start == '*') { - imported_T *imported = new_imported(gap != NULL ? gap + imported_T *imported; + + imported = find_imported(as_name, STRLEN(as_name), cctx); + if (imported != NULL && imported->imp_sid == sid) + { + if (imported->imp_flags & IMP_FLAGS_RELOAD) + // import already defined on a previous script load + imported->imp_flags &= ~IMP_FLAGS_RELOAD; + else + { + semsg(_(e_name_already_defined_str), as_name); + goto erret; + } + } + + imported = new_imported(gap != NULL ? gap : &SCRIPT_ITEM(import_sid)->sn_imports); - if (imported == NULL) goto erret; imported->imp_name = as_name; as_name = NULL; imported->imp_sid = sid; - imported->imp_all = TRUE; + imported->imp_flags = IMP_FLAGS_STAR; } else { @@ -479,6 +511,7 @@ handle_import( for (i = 0; i < names.ga_len; ++i) { char_u *name = ((char_u **)names.ga_data)[i]; + size_t len = STRLEN(name); int idx; imported_T *imported; ufunc_T *ufunc = NULL; @@ -489,28 +522,47 @@ handle_import( if (idx < 0 && ufunc == NULL) goto erret; - if (check_defined(name, STRLEN(name), cctx) == FAIL) - goto erret; - - imported = new_imported(gap != NULL ? gap - : &SCRIPT_ITEM(import_sid)->sn_imports); - if (imported == NULL) - goto erret; - - // TODO: check for "as" following - // imported->imp_name = vim_strsave(as_name); - imported->imp_name = name; - ((char_u **)names.ga_data)[i] = NULL; - imported->imp_sid = sid; - if (idx >= 0) + // If already imported with the same propertis and the + // IMP_FLAGS_RELOAD set then we keep that entry. Otherwise create + // a new one (and give an error for an existing import). + imported = find_imported(name, len, cctx); + if (imported != NULL + && (imported->imp_flags & IMP_FLAGS_RELOAD) + && imported->imp_sid == sid + && (idx >= 0 + ? (equal_type(imported->imp_type, type) + && imported->imp_var_vals_idx == idx) + : (equal_type(imported->imp_type, ufunc->uf_func_type) + && STRCMP(imported->imp_funcname, + ufunc->uf_name) == 0))) { - imported->imp_type = type; - imported->imp_var_vals_idx = idx; + imported->imp_flags &= ~IMP_FLAGS_RELOAD; } else { - imported->imp_type = ufunc->uf_func_type; - imported->imp_funcname = ufunc->uf_name; + if (check_defined(name, len, cctx) == FAIL) + goto erret; + + imported = new_imported(gap != NULL ? gap + : &SCRIPT_ITEM(import_sid)->sn_imports); + if (imported == NULL) + goto erret; + + // TODO: check for "as" following + // imported->imp_name = vim_strsave(as_name); + imported->imp_name = name; + ((char_u **)names.ga_data)[i] = NULL; + imported->imp_sid = sid; + if (idx >= 0) + { + imported->imp_type = type; + imported->imp_var_vals_idx = idx; + } + else + { + imported->imp_type = ufunc->uf_func_type; + imported->imp_funcname = ufunc->uf_name; + } } } }