diff src/vim9script.c @ 22594:209c7aa56325 v8.2.1845

patch 8.2.1845: Vim9: function defined in a block can't use block variables Commit: https://github.com/vim/vim/commit/8d739de43b84ef7817b3b5b826d1cbfe7572a62a Author: Bram Moolenaar <Bram@vim.org> Date: Wed Oct 14 19:39:19 2020 +0200 patch 8.2.1845: Vim9: function defined in a block can't use block variables 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.
author Bram Moolenaar <Bram@vim.org>
date Wed, 14 Oct 2020 19:45:04 +0200
parents 35ef9b0a81a3
children 107eae953b87
line wrap: on
line diff
--- a/src/vim9script.c
+++ b/src/vim9script.c
@@ -134,7 +134,7 @@ new_imported(garray_T *gap)
  * Free all imported items in script "sid".
  */
     void
-free_imports(int sid)
+free_imports_and_script_vars(int sid)
 {
     scriptitem_T    *si = SCRIPT_ITEM(sid);
     int		    idx;
@@ -146,7 +146,9 @@ free_imports(int sid)
 	vim_free(imp->imp_name);
     }
     ga_clear(&si->sn_imports);
-    ga_clear(&si->sn_var_vals);
+
+    free_all_script_vars(si);
+
     clear_type_list(&si->sn_type_list);
 }
 
@@ -565,6 +567,127 @@ vim9_declare_scriptvar(exarg_T *eap, cha
 }
 
 /*
+ * 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.
  */