Patch 8.2.1870

8 views
Skip to first unread message

Bram Moolenaar

unread,
Oct 20, 2020, 8:53:31 AM10/20/20
to vim...@googlegroups.com

Patch 8.2.1870
Problem: Vim9: no need to keep all script variables.
Solution: Only keep script variables when a function was defined that could
use them. Fix freeing static string on exit.
Files: src/vim9script.c, src/proto/vim9script.pro, src/structs.h,
src/ex_eval.c, src/userfunc.c, src/testdir/test_vim9_script.vim


*** ../vim-8.2.1869/src/vim9script.c 2020-10-15 12:46:38.733199522 +0200
--- src/vim9script.c 2020-10-20 14:21:10.515579929 +0200
***************
*** 51,58 ****

if (STRCMP(p_cpo, CPO_VIM) != 0)
{
! si->sn_save_cpo = p_cpo;
! p_cpo = vim_strsave((char_u *)CPO_VIM);
}
}

--- 51,58 ----

if (STRCMP(p_cpo, CPO_VIM) != 0)
{
! si->sn_save_cpo = vim_strsave(p_cpo);
! set_option_value((char_u *)"cpo", 0L, (char_u *)CPO_VIM, 0);
}
}

***************
*** 569,576 ****
}

/*
! * 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
--- 569,576 ----
}

/*
! * Vim9 part of adding a script variable: add it to sn_all_vars (lookup by name
! * with a hashtable) and sn_var_vals (lookup by index).
* When "type" is NULL use "tv" for the type.
*/
void
***************
*** 628,636 ****
/*
* Hide a script variable when leaving a block.
* "idx" is de index in sn_var_vals.
*/
void
! hide_script_var(scriptitem_T *si, int idx)
{
svar_T *sv = ((svar_T *)si->sn_var_vals.ga_data) + idx;
hashtab_T *script_ht = get_script_local_ht();
--- 628,638 ----
/*
* Hide a script variable when leaving a block.
* "idx" is de index in sn_var_vals.
+ * When "func_defined" is non-zero then a function was defined in this block,
+ * the variable may be accessed by it. Otherwise the variable can be cleared.
*/
void
! hide_script_var(scriptitem_T *si, int idx, int func_defined)
{
svar_T *sv = ((svar_T *)si->sn_var_vals.ga_data) + idx;
hashtab_T *script_ht = get_script_local_ht();
***************
*** 639,644 ****
--- 641,647 ----
hashitem_T *all_hi;

// Remove a variable declared inside the block, if it still exists.
+ // If it was added in a nested block it will already have been removed.
// 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);
***************
*** 646,664 ****
{
dictitem_T *di = HI2DI(script_hi);
sallvar_T *sav = HI2SAV(all_hi);

// There can be multiple entries with the same name in different
// blocks, find the right one.
while (sav != NULL && sav->sav_var_vals_idx != idx)
sav = sav->sav_next;
if (sav != NULL)
{
! 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);
- sv->sv_tv = &sav->sav_tv;
}
}
}
--- 649,684 ----
{
dictitem_T *di = HI2DI(script_hi);
sallvar_T *sav = HI2SAV(all_hi);
+ sallvar_T *sav_prev = NULL;

// There can be multiple entries with the same name in different
// blocks, find the right one.
while (sav != NULL && sav->sav_var_vals_idx != idx)
+ {
+ sav_prev = sav;
sav = sav->sav_next;
+ }
if (sav != NULL)
{
! if (func_defined)
! {
! // move the typval from the dictitem to the sallvar
! sav->sav_tv = di->di_tv;
! di->di_tv.v_type = VAR_UNKNOWN;
! sav->sav_flags = di->di_flags;
! sav->sav_di = NULL;
! sv->sv_tv = &sav->sav_tv;
! }
! else
! {
! if (sav_prev == NULL)
! hash_remove(all_ht, all_hi);
! else
! sav_prev->sav_next = sav->sav_next;
! sv->sv_name = NULL;
! vim_free(sav);
! }
delete_var(script_ht, script_hi);
}
}
}
*** ../vim-8.2.1869/src/proto/vim9script.pro 2020-10-15 12:46:38.733199522 +0200
--- src/proto/vim9script.pro 2020-10-20 13:09:35.996167733 +0200
***************
*** 9,15 ****
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, int idx);
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);
--- 9,15 ----
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, int idx, int func_defined);
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-8.2.1869/src/structs.h 2020-10-15 12:46:38.729199532 +0200
--- src/structs.h 2020-10-20 12:57:35.518235616 +0200
***************
*** 917,922 ****
--- 917,924 ----
# define CSF_SILENT 0x1000 // "emsg_silent" reset by ":try"
// Note that CSF_ELSE is only used when CSF_TRY and CSF_WHILE are unset
// (an ":if"), and CSF_SILENT is only used when CSF_TRY is set.
+ //
+ #define CSF_FUNC_DEF 0x2000 // a function was defined in this block

/*
* What's pending for being reactivated at the ":endtry" of this try
*** ../vim-8.2.1869/src/ex_eval.c 2020-10-15 12:46:38.733199522 +0200
--- src/ex_eval.c 2020-10-20 13:46:59.173728456 +0200
***************
*** 930,945 ****
{
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, i);
}

// TODO: is this needed?
--- 930,951 ----
{
scriptitem_T *si = SCRIPT_ITEM(current_sctx.sc_sid);
int i;
+ int func_defined =
+ cstack->cs_flags[cstack->cs_idx] & CSF_FUNC_DEF;

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;

+ // sv_name is set to NULL if it was already removed. This happens
+ // when it was defined in an inner block and no functions were
+ // defined there.
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
! // if "func_defined" is non-zero.
! hide_script_var(si, i, func_defined);
}

// TODO: is this needed?
*** ../vim-8.2.1869/src/userfunc.c 2020-10-15 12:46:38.733199522 +0200
--- src/userfunc.c 2020-10-20 14:07:39.257994178 +0200
***************
*** 3471,3497 ****

if (eap->cmdidx == CMD_def)
{
! int lnum_save = SOURCING_LNUM;

fp->uf_def_status = UF_TO_BE_COMPILED;

// error messages are for the first function line
SOURCING_LNUM = sourcing_lnum_top;

! if (eap->cstack != NULL && eap->cstack->cs_idx >= 0)
{
! int count = eap->cstack->cs_idx + 1;

// The block context may be needed for script variables declared in
! // a block visible now but not when the function is compiled.
fp->uf_block_ids = ALLOC_MULT(int, count);
if (fp->uf_block_ids != NULL)
{
! mch_memmove(fp->uf_block_ids, eap->cstack->cs_block_id,
sizeof(int) * count);
fp->uf_block_depth = count;
}
! // TODO: set flag in each block to indicate a function was defined
}

// parse the argument types
--- 3471,3505 ----

if (eap->cmdidx == CMD_def)
{
! int lnum_save = SOURCING_LNUM;
! cstack_T *cstack = eap->cstack;

fp->uf_def_status = UF_TO_BE_COMPILED;

// error messages are for the first function line
SOURCING_LNUM = sourcing_lnum_top;

! if (cstack != NULL && cstack->cs_idx >= 0)
{
! int count = cstack->cs_idx + 1;
! int i;

// The block context may be needed for script variables declared in
! // a block that is visible now but not when the function is called
! // later.
fp->uf_block_ids = ALLOC_MULT(int, count);
if (fp->uf_block_ids != NULL)
{
! mch_memmove(fp->uf_block_ids, cstack->cs_block_id,
sizeof(int) * count);
fp->uf_block_depth = count;
}
!
! // Set flag in each block to indicate a function was defined. This
! // is used to keep the variable when leaving the block, see
! // hide_script_var().
! for (i = 0; i <= cstack->cs_idx; ++i)
! cstack->cs_flags[i] |= CSF_FUNC_DEF;
}

// parse the argument types
*** ../vim-8.2.1869/src/testdir/test_vim9_script.vim 2020-10-17 22:04:04.118833463 +0200
--- src/testdir/test_vim9_script.vim 2020-10-20 14:10:57.777404737 +0200
***************
*** 296,301 ****
--- 296,320 ----
delete('Xdidit')
enddef

+ def Test_block_local_vars_with_func()
+ var lines =<< trim END
+ vim9script
+ if true
+ var foo = 'foo'
+ if true
+ var bar = 'bar'
+ def Func(): list<string>
+ return [foo, bar]
+ enddef
+ endif
+ endif
+ # function is compiled here, after blocks have finished, can still access
+ # "foo" and "bar"
+ assert_equal(['foo', 'bar'], Func())
+ END
+ CheckScriptSuccess(lines)
+ enddef
+
func g:NoSuchFunc()
echo 'none'
endfunc
*** ../vim-8.2.1869/src/version.c 2020-10-19 23:01:42.507664521 +0200
--- src/version.c 2020-10-20 14:22:16.879381525 +0200
***************
*** 752,753 ****
--- 752,755 ----
{ /* Add new patch number below this line */
+ /**/
+ 1870,
/**/

--
From "know your smileys":
[:-) Frankenstein's monster

/// Bram Moolenaar -- Br...@Moolenaar.net -- 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 ///
Reply all
Reply to author
Forward
0 new messages