Patch 8.2.1845
Problem: Vim9: function defined in a block can't use variables defined in
that block.
Solution: First step: Make a second hashtab that holds all script variables,
also block-local ones, with more information.
Files: src/structs.h, src/evalvars.c, src/ex_eval.c, src/vim9script.c,
src/proto/vim9script.pro, src/scriptfile.c
*** ../vim-8.2.1844/src/structs.h 2020-10-10 21:33:42.403033529 +0200
--- src/structs.h 2020-10-14 15:40:16.240730744 +0200
***************
*** 889,894 ****
--- 889,895 ----
} cs_pend;
void *cs_forinfo[CSTACK_LEN]; // info used by ":for"
int cs_line[CSTACK_LEN]; // line nr of ":while"/":for"
line
+ int cs_block_id[CSTACK_LEN]; // block ID stack
int cs_script_var_len[CSTACK_LEN]; // value of
sn_var_vals.ga_len
// when entering the block
int cs_idx; // current entry, or -1 if none
***************
*** 1701,1718 ****
* Holds the hashtab with variables local to each sourced script.
* Each item holds a variable (nameless) that points to the dict_T.
*/
! typedef struct
! {
dictitem_T sv_var;
dict_T sv_dict;
} scriptvar_T;
/*
* Entry for "sn_var_vals". Used for script-local variables.
*/
typedef struct {
! char_u *sv_name; // points into "sn_vars" di_key
! typval_T *sv_tv; // points into "sn_vars" di_tv
type_T *sv_type;
int sv_const;
int sv_export; // "export let var = val"
--- 1702,1748 ----
* Holds the hashtab with variables local to each sourced script.
* Each item holds a variable (nameless) that points to the dict_T.
*/
! typedef struct {
dictitem_T sv_var;
dict_T sv_dict;
} scriptvar_T;
/*
+ * Entry for "sn_all_vars". Contains the s: variables from sn_vars plus the
+ * block-local ones.
+ */
+ typedef struct sallvar_S sallvar_T;
+ struct sallvar_S {
+ sallvar_T *sav_next; // var with same name but different block
+ int sav_block_id; // block ID where declared
+ int sav_var_vals_idx; // index in sn_var_vals
+
+ // So long as the variable is valid (block it was defined in is still
+ // active) "sav_di" is used. It is set to NULL when leaving the block,
+ // then sav_tv and sav_flags are used.
+ dictitem_T *sav_di; // dictitem with di_key and di_tv
+ typval_T sav_tv; // type and value of the variable
+ char_u sav_flags; // DI_FLAGS_ flags (only used for variable)
+ char_u sav_key[1]; // key (actually longer!)
+ };
+
+ /*
+ * In the sn_all_vars hashtab item "hi_key" points to "sav_key" in a
sallvar_T.
+ * This makes it possible to store and find the sallvar_T.
+ * SAV2HIKEY() converts a sallvar_T pointer to a hashitem key pointer.
+ * HIKEY2SAV() converts a hashitem key pointer to a sallvar_T pointer.
+ * HI2SAV() converts a hashitem pointer to a sallvar_T pointer.
+ */
+ #define SAV2HIKEY(sav) ((sav)->sav_key)
+ #define HIKEY2SAV(p) ((sallvar_T *)(p - offsetof(sallvar_T, sav_key)))
+ #define HI2SAV(hi) HIKEY2SAV((hi)->hi_key)
+
+ /*
* Entry for "sn_var_vals". Used for script-local variables.
*/
typedef struct {
! char_u *sv_name; // points into "sn_all_vars" di_key
! typval_T *sv_tv; // points into "sn_vars" or "sn_all_vars" di_tv
type_T *sv_type;
int sv_const;
int sv_export; // "export let var = val"
***************
*** 1742,1753 ****
{
char_u *sn_name;
! scriptvar_T *sn_vars; // stores s: variables for this script
! garray_T sn_var_vals; // same variables as a list of svar_T
garray_T sn_imports; // imported items, imported_T
-
garray_T sn_type_list; // keeps types used by variables
int sn_version; // :scriptversion
int sn_had_command; // TRUE if any command was executed
--- 1772,1798 ----
{
char_u *sn_name;
! // "sn_vars" stores the s: variables currently valid. When leaving a
block
! // variables local to that block are removed.
! scriptvar_T *sn_vars;
!
! // Specific for a Vim9 script.
! // "sn_all_vars" stores all script variables ever declared. So long as
the
! // variable is still valid the value is in "sn_vars->sv_dict...di_tv".
! // When the block of a declaration is left the value is moved to
! // "sn_all_vars..sav_tv".
! // Variables with duplicate names are possible, the sav_block_id must be
! // used to check that which variable is valid.
! dict_T sn_all_vars; // all script variables, dict of sallvar_T
!
! // Stores the same variables as in "sn_all_vars" as a list of svar_T, so
! // that they can be quickly found by index instead of a hash table lookup.
! // Also stores the type.
! garray_T sn_var_vals;
garray_T sn_imports; // imported items, imported_T
garray_T sn_type_list; // keeps types used by variables
+ int sn_current_block_id; // Unique ID for each script block
int sn_version; // :scriptversion
int sn_had_command; // TRUE if any command was executed
*** ../vim-8.2.1844/src/evalvars.c 2020-10-10 19:07:04.187713866 +0200
--- src/evalvars.c 2020-10-14 18:51:10.342095521 +0200
***************
*** 2882,2888 ****
}
}
hash_clear(ht);
! ht->ht_used = 0;
}
/*
--- 2882,2888 ----
}
}
hash_clear(ht);
! hash_init(ht);
}
/*
***************
*** 3142,3171 ****
if (flags & ASSIGN_CONST)
di->di_flags |= DI_FLAGS_LOCK;
if (is_script_local && vim9script)
! {
! scriptitem_T *si = SCRIPT_ITEM(current_sctx.sc_sid);
!
! // Store a pointer to the typval_T, so that it can be found by
! // index instead of using a hastab lookup.
! if (ga_grow(&si->sn_var_vals, 1) == OK)
! {
! svar_T *sv = ((svar_T *)si->sn_var_vals.ga_data)
! + si->sn_var_vals.ga_len;
! sv->sv_name = di->di_key;
! sv->sv_tv = &di->di_tv;
! if (type == NULL)
! sv->sv_type = typval2type(tv, &si->sn_type_list);
! else
! sv->sv_type = type;
! sv->sv_const = (flags & ASSIGN_CONST);
! sv->sv_export = is_export;
! ++si->sn_var_vals.ga_len;
!
! // let ex_export() know the export worked.
! is_export = FALSE;
! }
! }
}
if (copy || tv->v_type == VAR_NUMBER || tv->v_type == VAR_FLOAT)
--- 3142,3151 ----
if (flags & ASSIGN_CONST)
di->di_flags |= DI_FLAGS_LOCK;
+ // A Vim9 script-local variable is also added to sn_all_vars and
+ // sn_var_vals.
if (is_script_local && vim9script)
! add_vim9_script_var(di, tv, type);
}
if (copy || tv->v_type == VAR_NUMBER || tv->v_type == VAR_FLOAT)
*** ../vim-8.2.1844/src/ex_eval.c 2020-10-10 21:33:42.403033529 +0200
--- src/ex_eval.c 2020-10-14 19:29:29.494435197 +0200
***************
*** 914,954 ****
{
++cstack->cs_idx;
if (in_vim9script())
! cstack->cs_script_var_len[cstack->cs_idx] =
! SCRIPT_ITEM(current_sctx.sc_sid)->sn_var_vals.ga_len;
}
static void
leave_block(cstack_T *cstack)
{
! int i;
!
! if (in_vim9script())
{
scriptitem_T *si = SCRIPT_ITEM(current_sctx.sc_sid);
! hashtab_T *ht = get_script_local_ht();
! if (ht != NULL)
! {
! for (i = cstack->cs_script_var_len[cstack->cs_idx];
i < si->sn_var_vals.ga_len; ++i)
! {
! svar_T *sv = ((svar_T *)si->sn_var_vals.ga_data) + i;
! hashitem_T *hi;
! if (sv->sv_name != NULL)
! {
! // Remove a variable declared inside the block, if it still
! // exists.
! hi = hash_find(ht, sv->sv_name);
! if (!HASHITEM_EMPTY(hi))
! {
! delete_var(ht, hi);
! sv->sv_name = NULL;
! }
! }
! }
}
}
--cstack->cs_idx;
}
--- 914,948 ----
{
++cstack->cs_idx;
if (in_vim9script())
! {
! scriptitem_T *si = SCRIPT_ITEM(current_sctx.sc_sid);
!
! cstack->cs_script_var_len[cstack->cs_idx] = si->sn_var_vals.ga_len;
! cstack->cs_block_id[cstack->cs_idx] = ++si->sn_current_block_id;
! }
}
static void
leave_block(cstack_T *cstack)
{
! if (in_vim9script() && SCRIPT_ID_VALID(current_sctx.sc_sid))
{
scriptitem_T *si = SCRIPT_ITEM(current_sctx.sc_sid);
! int i;
! for (i = cstack->cs_script_var_len[cstack->cs_idx];
i < si->sn_var_vals.ga_len; ++i)
! {
! svar_T *sv = ((svar_T *)si->sn_var_vals.ga_data) + i;
! if (sv->sv_name != NULL)
! // Remove a variable declared inside the block, if it still
! // exists, from sn_vars and move the value into sn_all_vars.
! hide_script_var(si, sv);
}
+
+ // TODO: is this needed?
+ cstack->cs_script_var_len[cstack->cs_idx] = si->sn_var_vals.ga_len;
}
--cstack->cs_idx;
}
*** ../vim-8.2.1844/src/vim9script.c 2020-10-08 21:16:38.643643838 +0200
--- src/vim9script.c 2020-10-14 19:28:58.062506167 +0200
***************
*** 134,140 ****
* Free all imported items in script "sid".
*/
void
! free_imports(int sid)
{
scriptitem_T *si = SCRIPT_ITEM(sid);
int idx;
--- 134,140 ----
* Free all imported items in script "sid".
*/
void
! free_imports_and_script_vars(int sid)
{
scriptitem_T *si = SCRIPT_ITEM(sid);
int idx;
***************
*** 146,152 ****
vim_free(imp->imp_name);
}
ga_clear(&si->sn_imports);
! ga_clear(&si->sn_var_vals);
clear_type_list(&si->sn_type_list);
}
--- 146,154 ----
vim_free(imp->imp_name);
}
ga_clear(&si->sn_imports);
!
! free_all_script_vars(si);
!
clear_type_list(&si->sn_type_list);
}
***************
*** 565,570 ****
--- 567,693 ----
}
/*
+ * Vim9 part of adding a script variable: add it to sn_all_vars and
+ * sn_var_vals.
+ * When "type" is NULL use "tv" for the type.
+ */
+ void
+ add_vim9_script_var(dictitem_T *di, typval_T *tv, type_T *type)
+ {
+ scriptitem_T *si = SCRIPT_ITEM(current_sctx.sc_sid);
+
+ // Store a pointer to the typval_T, so that it can be found by
+ // index instead of using a hastab lookup.
+ if (ga_grow(&si->sn_var_vals, 1) == OK)
+ {
+ svar_T *sv = ((svar_T *)si->sn_var_vals.ga_data)
+ + si->sn_var_vals.ga_len;
+ hashitem_T *hi;
+ sallvar_T *newsav = (sallvar_T *)alloc_clear(
+ sizeof(sallvar_T) + STRLEN(di->di_key));
+
+ if (newsav == NULL)
+ return;
+
+ sv->sv_tv = &di->di_tv;
+ if (type == NULL)
+ sv->sv_type = typval2type(tv, &si->sn_type_list);
+ else
+ sv->sv_type = type;
+ sv->sv_const = (di->di_flags & DI_FLAGS_LOCK) ? ASSIGN_CONST : 0;
+ sv->sv_export = is_export;
+ newsav->sav_var_vals_idx = si->sn_var_vals.ga_len;
+ ++si->sn_var_vals.ga_len;
+
+ STRCPY(&newsav->sav_key, di->di_key);
+ sv->sv_name = newsav->sav_key;
+ newsav->sav_di = di;
+ newsav->sav_block_id = si->sn_current_block_id;
+
+ hi = hash_find(&si->sn_all_vars.dv_hashtab, newsav->sav_key);
+ if (!HASHITEM_EMPTY(hi))
+ {
+ sallvar_T *sav = HI2SAV(hi);
+
+ // variable with this name exists in another block
+ while (sav->sav_next != NULL)
+ sav = sav->sav_next;
+ sav->sav_next = newsav;
+ }
+ else
+ // new variable name
+ hash_add(&si->sn_all_vars.dv_hashtab, newsav->sav_key);
+
+ // let ex_export() know the export worked.
+ is_export = FALSE;
+ }
+ }
+
+ void
+ hide_script_var(scriptitem_T *si, svar_T *sv)
+ {
+ hashtab_T *script_ht = get_script_local_ht();
+ hashtab_T *all_ht = &si->sn_all_vars.dv_hashtab;
+ hashitem_T *script_hi;
+ hashitem_T *all_hi;
+
+ // Remove a variable declared inside the block, if it still exists.
+ // The typval is moved into the sallvar_T.
+ script_hi = hash_find(script_ht, sv->sv_name);
+ all_hi = hash_find(all_ht, sv->sv_name);
+ if (!HASHITEM_EMPTY(script_hi) && !HASHITEM_EMPTY(all_hi))
+ {
+ dictitem_T *di = HI2DI(script_hi);
+ sallvar_T *sav = HI2SAV(all_hi);
+
+ sav->sav_tv = di->di_tv;
+ di->di_tv.v_type = VAR_UNKNOWN;
+ sav->sav_flags = di->di_flags;
+ sav->sav_di = NULL;
+ delete_var(script_ht, script_hi);
+ }
+ }
+
+ /*
+ * Free the script variables from "sn_all_vars".
+ */
+ void
+ free_all_script_vars(scriptitem_T *si)
+ {
+ int todo;
+ hashtab_T *ht = &si->sn_all_vars.dv_hashtab;
+ hashitem_T *hi;
+ sallvar_T *sav;
+ sallvar_T *sav_next;
+
+ hash_lock(ht);
+ todo = (int)ht->ht_used;
+ for (hi = ht->ht_array; todo > 0; ++hi)
+ {
+ if (!HASHITEM_EMPTY(hi))
+ {
+ --todo;
+
+ // Free the variable. Don't remove it from the hashtab, ht_array
+ // might change then. hash_clear() takes care of it later.
+ sav = HI2SAV(hi);
+ while (sav != NULL)
+ {
+ sav_next = sav->sav_next;
+ if (sav->sav_di == NULL)
+ clear_tv(&sav->sav_tv);
+ vim_free(sav);
+ sav = sav_next;
+ }
+ }
+ }
+ hash_clear(ht);
+ hash_init(ht);
+
+ ga_clear(&si->sn_var_vals);
+ }
+
+ /*
* Find the script-local variable that links to "dest".
* Returns NULL if not found.
*/
*** ../vim-8.2.1844/src/proto/vim9script.pro 2020-10-08 21:16:38.643643838
+0200
--- src/proto/vim9script.pro 2020-10-14 18:51:51.665872905 +0200
***************
*** 3,13 ****
void ex_vim9script(exarg_T *eap);
int not_in_vim9(exarg_T *eap);
void ex_export(exarg_T *eap);
! void free_imports(int sid);
void ex_import(exarg_T *eap);
int find_exported(int sid, char_u *name, ufunc_T **ufunc, type_T **type);
char_u *handle_import(char_u *arg_start, garray_T *gap, int import_sid,
evalarg_T *evalarg, void *cctx);
char_u *vim9_declare_scriptvar(exarg_T *eap, char_u *arg);
svar_T *find_typval_in_script(typval_T *dest);
int check_script_var_type(typval_T *dest, typval_T *value, char_u *name);
/* vim: set ft=c : */
--- 3,16 ----
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 ex_import(exarg_T *eap);
int find_exported(int sid, char_u *name, ufunc_T **ufunc, type_T **type);
char_u *handle_import(char_u *arg_start, garray_T *gap, int import_sid,
evalarg_T *evalarg, void *cctx);
char_u *vim9_declare_scriptvar(exarg_T *eap, char_u *arg);
+ void add_vim9_script_var(dictitem_T *di, typval_T *tv, type_T *type);
+ void hide_script_var(scriptitem_T *si, svar_T *sv);
+ void free_all_script_vars(scriptitem_T *si);
svar_T *find_typval_in_script(typval_T *dest);
int check_script_var_type(typval_T *dest, typval_T *value, char_u *name);
/* vim: set ft=c : */
*** ../vim-8.2.1844/src/scriptfile.c 2020-09-11 17:59:19.032235034 +0200
--- src/scriptfile.c 2020-10-14 17:52:46.130048446 +0200
***************
*** 1348,1355 ****
}
}
! // old imports are no longer valid
! free_imports(sid);
// in Vim9 script functions are marked deleted
if (is_vim9)
--- 1348,1355 ----
}
}
! // old imports and script variables are no longer valid
! free_imports_and_script_vars(sid);
// in Vim9 script functions are marked deleted
if (is_vim9)
***************
*** 1375,1380 ****
--- 1375,1381 ----
// Allocate the local script variables to use for this script.
new_script_vars(script_items.ga_len);
ga_init2(&si->sn_var_vals, sizeof(svar_T), 10);
+ hash_init(&si->sn_all_vars.dv_hashtab);
ga_init2(&si->sn_imports, sizeof(imported_T), 10);
ga_init2(&si->sn_type_list, sizeof(type_T), 10);
# ifdef FEAT_PROFILE
***************
*** 1592,1598 ****
vim_free(si->sn_vars);
vim_free(si->sn_name);
! free_imports(i);
free_string_option(si->sn_save_cpo);
# ifdef FEAT_PROFILE
ga_clear(&si->sn_prl_ga);
--- 1593,1599 ----
vim_free(si->sn_vars);
vim_free(si->sn_name);
! free_imports_and_script_vars(i);
free_string_option(si->sn_save_cpo);
# ifdef FEAT_PROFILE
ga_clear(&si->sn_prl_ga);
*** ../vim-8.2.1844/src/version.c 2020-10-13 22:15:37.486726632 +0200
--- src/version.c 2020-10-14 19:38:11.441167357 +0200
***************
*** 752,753 ****
--- 752,755 ----
{ /* Add new patch number below this line */
+ /**/
+ 1845,
/**/
--
>From "know your smileys":
y:-) Bad toupee
/// Bram Moolenaar -- [email protected] -- http://www.Moolenaar.net \\\
/// sponsor Vim, vote for features -- http://www.Vim.org/sponsor/ \\\
\\\ an exciting new programming language -- http://www.Zimbu.org ///
\\\ help me help AIDS victims -- http://ICCF-Holland.org ///
--
--
You received this message from the "vim_dev" maillist.
Do not top-post! Type your reply below the text you are replying to.
For more information, visit http://www.vim.org/maillist.php
---
You received this message because you are subscribed to the Google Groups
"vim_dev" group.
To unsubscribe from this group and stop receiving emails from it, send an email
to [email protected].
To view this discussion on the web visit
https://groups.google.com/d/msgid/vim_dev/202010141739.09EHdlRT647730%40masaka.moolenaar.net.