From 84f6dc7ed29d52b1034bfb27bdcd3154a01ee2b1 Mon Sep 17 00:00:00 2001 From: Ernie Rael Date: Sun, 21 Apr 2024 14:45:48 +0200 Subject: patch 9.1.0360: Vim9: does not handle autoloaded variables well Problem: Vim9: does not handle autoloaded variables well Solution: Better handle script-level exported variable references from autoload files (Ernie Rael). fixes: #14591 closes: #14607 Signed-off-by: Ernie Rael Signed-off-by: Christian Brabandt --- src/evalvars.c | 74 ++++++++++++++++++++++++++++++++-------- src/proto/evalvars.pro | 1 + src/testdir/test_vim9_import.vim | 65 +++++++++++++++++++++++++++++++++++ src/version.c | 2 ++ 4 files changed, 128 insertions(+), 14 deletions(-) diff --git a/src/evalvars.c b/src/evalvars.c index 70bb6da2ce..b70a3cd394 100644 --- a/src/evalvars.c +++ b/src/evalvars.c @@ -3340,12 +3340,31 @@ find_var(char_u *name, hashtab_T **htp, int no_autoload) } } + // and finally try + return find_var_autoload_prefix(name, 0, htp, NULL); +} + +/* + * Find variable "name" with sn_autoload_prefix. + * Return a pointer to it if found, NULL if not found. + * When "sid" > 0, use it otherwise use "current_sctx.sc_sid". + * When "htp" is not NULL set "htp" to the hashtab_T used. + * When "namep" is not NULL set "namep" to the generated name, and + * then the caller gets ownership and is responsible for freeing the name. + */ + dictitem_T * +find_var_autoload_prefix(char_u *name, int sid, hashtab_T **htp, + char_u **namep) +{ + hashtab_T *ht; + dictitem_T *ret = NULL; // When using "vim9script autoload" script-local items are prefixed but can // be used with s:name. - if (SCRIPT_ID_VALID(current_sctx.sc_sid) + int check_sid = sid > 0 ? sid : current_sctx.sc_sid; + if (SCRIPT_ID_VALID(check_sid) && (in_vim9script() || (name[0] == 's' && name[1] == ':'))) { - scriptitem_T *si = SCRIPT_ITEM(current_sctx.sc_sid); + scriptitem_T *si = SCRIPT_ITEM(check_sid); if (si->sn_autoload_prefix != NULL) { @@ -3355,20 +3374,26 @@ find_var(char_u *name, hashtab_T **htp, int no_autoload) if (auto_name != NULL) { + int free_auto_name = TRUE; ht = &globvarht; ret = find_var_in_ht(ht, 'g', auto_name, TRUE); - vim_free(auto_name); if (ret != NULL) { if (htp != NULL) *htp = ht; - return ret; + if (namep != NULL) + { + free_auto_name = FALSE; + *namep = auto_name; + } } + if (free_auto_name) + vim_free(auto_name); } } } - return NULL; + return ret; } /* @@ -3508,7 +3533,11 @@ lookup_scriptitem( hi = hash_find(ht, p); res = HASHITEM_EMPTY(hi) ? FAIL : OK; - // if not script-local, then perhaps imported + // if not script-local, then perhaps autoload-exported + if (res == FAIL && find_var_autoload_prefix(p, 0, NULL, NULL) != NULL) + res = OK; + + // if not script-local or autoload, then perhaps imported if (res == FAIL && find_imported(p, 0, FALSE) != NULL) res = OK; if (p != buffer) @@ -3904,23 +3933,40 @@ set_var_const( if (sid != 0) { + varname = NULL; if (SCRIPT_ID_VALID(sid)) - ht = &SCRIPT_VARS(sid); - varname = name; + { + char_u *auto_name = NULL; + if (find_var_autoload_prefix(name, sid, &ht, &auto_name) != NULL) + { + var_in_autoload = TRUE; + varname = auto_name; + name_tofree = varname; + } + else + ht = &SCRIPT_VARS(sid); + } + if (varname == NULL) + varname = name; } else { - scriptitem_T *si; + scriptitem_T *si; + char_u *auto_name = NULL; - if (in_vim9script() && is_export - && SCRIPT_ID_VALID(current_sctx.sc_sid) - && (si = SCRIPT_ITEM(current_sctx.sc_sid)) - ->sn_autoload_prefix != NULL) + if (in_vim9script() + && SCRIPT_ID_VALID(current_sctx.sc_sid) + && (si = SCRIPT_ITEM(current_sctx.sc_sid)) + ->sn_autoload_prefix != NULL + && (is_export + || find_var_autoload_prefix(name, 0, NULL, &auto_name) + != NULL)) { // In a vim9 autoload script an exported variable is put in the // global namespace with the autoload prefix. var_in_autoload = TRUE; - varname = concat_str(si->sn_autoload_prefix, name); + varname = auto_name != NULL ? auto_name + : concat_str(si->sn_autoload_prefix, name); if (varname == NULL) goto failed; name_tofree = varname; diff --git a/src/proto/evalvars.pro b/src/proto/evalvars.pro index ea14fe5cef..a0e0100016 100644 --- a/src/proto/evalvars.pro +++ b/src/proto/evalvars.pro @@ -63,6 +63,7 @@ int eval_variable(char_u *name, int len, scid_T sid, typval_T *rettv, dictitem_T int eval_variable_import(char_u *name, typval_T *rettv); void check_vars(char_u *name, int len); dictitem_T *find_var(char_u *name, hashtab_T **htp, int no_autoload); +dictitem_T *find_var_autoload_prefix(char_u *name, int sid, hashtab_T **htp, char_u **namep); dictitem_T *find_var_also_in_script(char_u *name, hashtab_T **htp, int no_autoload); dictitem_T *find_var_in_ht(hashtab_T *ht, int htname, char_u *varname, int no_autoload); hashtab_T *get_script_local_ht(void); diff --git a/src/testdir/test_vim9_import.vim b/src/testdir/test_vim9_import.vim index 78383ee79b..cfab50d584 100644 --- a/src/testdir/test_vim9_import.vim +++ b/src/testdir/test_vim9_import.vim @@ -1201,6 +1201,71 @@ def Test_autoload_import_relative_from_buffer_in_dir() :bw! enddef +" Test modifying exported autoload variable. Github issue: #14591 +def Test_autoload_export_variables() + mkdir('Xautoload_vars/autoload', 'pR') + var lines =<< trim END + vim9script + export var val = 11 + val = 42 + END + + # Test that the imported script, above, can modify the exported variable; + # and test that the importing script, below, can modify the variable. + writefile(lines, 'Xautoload_vars/autoload/Xauto_vars_f2.vim', 'D') + lines =<< trim END + vim9script + + import autoload './Xautoload_vars/autoload/Xauto_vars_f2.vim' as f2 + + def F(): number + return f2.val + enddef + assert_equal(42, F()) + assert_equal(42, f2.val) + f2.val = 17 + assert_equal(17, f2.val) + + def G() + f2.val = 19 + enddef + G() + assert_equal(19, f2.val) + END + v9.CheckScriptSuccess(lines) + + # Test const var is not modifiable. + lines =<< trim END + vim9script + export const val = 11 + val = 42 + END + writefile(lines, 'Xautoload_vars/autoload/Xauto_vars_f3.vim', 'D') + lines =<< trim END + vim9script + + import autoload './Xautoload_vars/autoload/Xauto_vars_f3.vim' as f3 + + var x = f3.val + END + v9.CheckScriptFailure(lines, 'E46:') + + # Test const var is not modifiable from importing script. + lines =<< trim END + vim9script + export const val = 11 + END + writefile(lines, 'Xautoload_vars/autoload/Xauto_vars_f4.vim', 'D') + lines =<< trim END + vim9script + + import autoload './Xautoload_vars/autoload/Xauto_vars_f4.vim' as f4 + + f4.val = 13 + END + v9.CheckScriptFailure(lines, 'E46:') +enddef + def Test_autoload_import_relative_autoload_dir() mkdir('autoload', 'pR') var lines =<< trim END diff --git a/src/version.c b/src/version.c index 9e2acb87e2..d418b602b2 100644 --- a/src/version.c +++ b/src/version.c @@ -704,6 +704,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ +/**/ + 360, /**/ 359, /**/ -- cgit v1.2.3