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 -- [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/202010201253.09KCrDGe1393454%40masaka.moolenaar.net.

Raspunde prin e-mail lui