Mercurial > vim
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. */