Patch 8.2.2650
Problem:    Vim9: command modifiers not handled in nested function.
Solution:   Keep function-local info in a structure and save it on the stack.
Files:      src/vim9execute.c, src/vim9.h, src/testdir/test_vim9_func.vim


*** ../vim-8.2.2649/src/vim9execute.c   2021-03-17 15:02:52.170478171 +0100
--- src/vim9execute.c   2021-03-24 21:51:06.577057287 +0100
***************
*** 154,159 ****
--- 154,168 ----
      return OK;
  }
  
+ // Data local to a function.
+ // On a function call, if not empty, is saved on the stack and restored when
+ // returning.
+ typedef struct {
+     int               floc_restore_cmdmod;
+     cmdmod_T  floc_save_cmdmod;
+     int               floc_restore_cmdmod_stacklen;
+ } funclocal_T;
+ 
  /*
   * Call compiled function "cdf_idx" from compiled code.
   * This adds a stack frame and sets the instruction pointer to the start of 
the
***************
*** 170,185 ****
   * - reserved space for local variables
   */
      static int
! call_dfunc(int cdf_idx, partial_T *pt, int argcount_arg, ectx_T *ectx)
  {
!     int           argcount = argcount_arg;
!     dfunc_T *dfunc = ((dfunc_T *)def_functions.ga_data) + cdf_idx;
!     ufunc_T *ufunc = dfunc->df_ufunc;
!     int           arg_to_add;
!     int           vararg_count = 0;
!     int           varcount;
!     int           idx;
!     estack_T *entry;
  
      if (dfunc->df_deleted)
      {
--- 179,200 ----
   * - reserved space for local variables
   */
      static int
! call_dfunc(
!       int             cdf_idx,
!       partial_T       *pt,
!       int             argcount_arg,
!       funclocal_T     *funclocal,
!       ectx_T          *ectx)
  {
!     int               argcount = argcount_arg;
!     dfunc_T   *dfunc = ((dfunc_T *)def_functions.ga_data) + cdf_idx;
!     ufunc_T   *ufunc = dfunc->df_ufunc;
!     int               arg_to_add;
!     int               vararg_count = 0;
!     int               varcount;
!     int               idx;
!     estack_T  *entry;
!     funclocal_T       *floc = NULL;
  
      if (dfunc->df_deleted)
      {
***************
*** 267,272 ****
--- 282,297 ----
      if (funcdepth_increment() == FAIL)
        return FAIL;
  
+     // Only make a copy of funclocal if it contains something to restore.
+     if (funclocal->floc_restore_cmdmod)
+     {
+       floc = ALLOC_ONE(funclocal_T);
+       if (floc == NULL)
+           return FAIL;
+       *floc = *funclocal;
+       funclocal->floc_restore_cmdmod = FALSE;
+     }
+ 
      // Move the vararg-list to below the missing optional arguments.
      if (vararg_count > 0 && arg_to_add > 0)
        *STACK_TV_BOT(arg_to_add - 1) = *STACK_TV_BOT(-1);
***************
*** 280,285 ****
--- 305,311 ----
      STACK_TV_BOT(STACK_FRAME_FUNC_OFF)->vval.v_number = ectx->ec_dfunc_idx;
      STACK_TV_BOT(STACK_FRAME_IIDX_OFF)->vval.v_number = ectx->ec_iidx;
      STACK_TV_BOT(STACK_FRAME_OUTER_OFF)->vval.v_string = (void 
*)ectx->ec_outer;
+     STACK_TV_BOT(STACK_FRAME_FUNCLOCAL_OFF)->vval.v_string = (void *)floc;
      STACK_TV_BOT(STACK_FRAME_IDX_OFF)->vval.v_number = ectx->ec_frame_idx;
      ectx->ec_frame_idx = ectx->ec_stack.ga_len;
  
***************
*** 530,536 ****
   * Return from the current function.
   */
      static int
! func_return(ectx_T *ectx)
  {
      int               idx;
      int               ret_idx;
--- 556,562 ----
   * Return from the current function.
   */
      static int
! func_return(funclocal_T *funclocal, ectx_T *ectx)
  {
      int               idx;
      int               ret_idx;
***************
*** 543,548 ****
--- 569,575 ----
                                        + STACK_FRAME_FUNC_OFF)->vval.v_number;
      dfunc_T   *prev_dfunc = ((dfunc_T *)def_functions.ga_data)
                                                              + prev_dfunc_idx;
+     funclocal_T       *floc;
  
  #ifdef FEAT_PROFILE
      if (do_profiling == PROF_YES)
***************
*** 592,602 ****
--- 619,639 ----
                                        + STACK_FRAME_IIDX_OFF)->vval.v_number;
      ectx->ec_outer = (void *)STACK_TV(ectx->ec_frame_idx
                                       + STACK_FRAME_OUTER_OFF)->vval.v_string;
+     floc = (void *)STACK_TV(ectx->ec_frame_idx
+                                  + STACK_FRAME_FUNCLOCAL_OFF)->vval.v_string;
      // restoring ec_frame_idx must be last
      ectx->ec_frame_idx = STACK_TV(ectx->ec_frame_idx
                                       + STACK_FRAME_IDX_OFF)->vval.v_number;
      ectx->ec_instr = INSTRUCTIONS(prev_dfunc);
  
+     if (floc == NULL)
+       funclocal->floc_restore_cmdmod = FALSE;
+     else
+     {
+       *funclocal = *floc;
+       vim_free(floc);
+     }
+ 
      if (ret_idx > 0)
      {
        // Reset the stack to the position before the call, with a spot for the
***************
*** 690,695 ****
--- 727,733 ----
        ufunc_T     *ufunc,
        partial_T   *pt,
        int         argcount,
+       funclocal_T *funclocal,
        ectx_T      *ectx,
        isn_T       *iptr)
  {
***************
*** 729,735 ****
            iptr->isn_arg.dfunc.cdf_idx = ufunc->uf_dfunc_idx;
            iptr->isn_arg.dfunc.cdf_argcount = argcount;
        }
!       return call_dfunc(ufunc->uf_dfunc_idx, pt, argcount, ectx);
      }
  
      if (call_prepare(argcount, argvars, ectx) == FAIL)
--- 767,773 ----
            iptr->isn_arg.dfunc.cdf_idx = ufunc->uf_dfunc_idx;
            iptr->isn_arg.dfunc.cdf_argcount = argcount;
        }
!       return call_dfunc(ufunc->uf_dfunc_idx, pt, argcount, funclocal, ectx);
      }
  
      if (call_prepare(argcount, argvars, ectx) == FAIL)
***************
*** 773,779 ****
   * Returns FAIL if not found without an error message.
   */
      static int
! call_by_name(char_u *name, int argcount, ectx_T *ectx, isn_T *iptr)
  {
      ufunc_T *ufunc;
  
--- 811,822 ----
   * Returns FAIL if not found without an error message.
   */
      static int
! call_by_name(
!       char_u      *name,
!       int         argcount,
!       funclocal_T *funclocal,
!       ectx_T      *ectx,
!       isn_T       *iptr)
  {
      ufunc_T *ufunc;
  
***************
*** 824,837 ****
            }
        }
  
!       return call_ufunc(ufunc, NULL, argcount, ectx, iptr);
      }
  
      return FAIL;
  }
  
      static int
! call_partial(typval_T *tv, int argcount_arg, ectx_T *ectx)
  {
      int               argcount = argcount_arg;
      char_u    *name = NULL;
--- 867,884 ----
            }
        }
  
!       return call_ufunc(ufunc, NULL, argcount, funclocal, ectx, iptr);
      }
  
      return FAIL;
  }
  
      static int
! call_partial(
!       typval_T    *tv,
!       int         argcount_arg,
!       funclocal_T *funclocal,
!       ectx_T      *ectx)
  {
      int               argcount = argcount_arg;
      char_u    *name = NULL;
***************
*** 860,866 ****
        }
  
        if (pt->pt_func != NULL)
!           return call_ufunc(pt->pt_func, pt, argcount, ectx, NULL);
  
        name = pt->pt_name;
      }
--- 907,913 ----
        }
  
        if (pt->pt_func != NULL)
!           return call_ufunc(pt->pt_func, pt, argcount, funclocal, ectx, NULL);
  
        name = pt->pt_name;
      }
***************
*** 878,884 ****
        if (error != FCERR_NONE)
            res = FAIL;
        else
!           res = call_by_name(fname, argcount, ectx, NULL);
        vim_free(tofree);
      }
  
--- 925,931 ----
        if (error != FCERR_NONE)
            res = FAIL;
        else
!           res = call_by_name(fname, argcount, funclocal, ectx, NULL);
        vim_free(tofree);
      }
  
***************
*** 1125,1136 ****
   * "iptr" can be used to replace the instruction with a more efficient one.
   */
      static int
! call_eval_func(char_u *name, int argcount, ectx_T *ectx, isn_T *iptr)
  {
      int           called_emsg_before = called_emsg;
      int           res;
  
!     res = call_by_name(name, argcount, ectx, iptr);
      if (res == FAIL && called_emsg == called_emsg_before)
      {
        dictitem_T      *v;
--- 1172,1188 ----
   * "iptr" can be used to replace the instruction with a more efficient one.
   */
      static int
! call_eval_func(
!       char_u      *name,
!       int         argcount,
!       funclocal_T *funclocal,
!       ectx_T      *ectx,
!       isn_T       *iptr)
  {
      int           called_emsg_before = called_emsg;
      int           res;
  
!     res = call_by_name(name, argcount, funclocal, ectx, iptr);
      if (res == FAIL && called_emsg == called_emsg_before)
      {
        dictitem_T      *v;
***************
*** 1146,1152 ****
            semsg(_(e_unknownfunc), name);
            return FAIL;
        }
!       return call_partial(&v->di_tv, argcount, ectx);
      }
      return res;
  }
--- 1198,1204 ----
            semsg(_(e_unknownfunc), name);
            return FAIL;
        }
!       return call_partial(&v->di_tv, argcount, funclocal, ectx);
      }
      return res;
  }
***************
*** 1222,1230 ****
      int               save_suppress_errthrow = suppress_errthrow;
      msglist_T **saved_msg_list = NULL;
      msglist_T *private_msg_list = NULL;
!     cmdmod_T  save_cmdmod;
!     int               restore_cmdmod = FALSE;
!     int               restore_cmdmod_stacklen = 0;
      int               save_emsg_silent_def = emsg_silent_def;
      int               save_did_emsg_def = did_emsg_def;
      int               trylevel_at_start = trylevel;
--- 1274,1280 ----
      int               save_suppress_errthrow = suppress_errthrow;
      msglist_T **saved_msg_list = NULL;
      msglist_T *private_msg_list = NULL;
!     funclocal_T funclocal;
      int               save_emsg_silent_def = emsg_silent_def;
      int               save_did_emsg_def = did_emsg_def;
      int               trylevel_at_start = trylevel;
***************
*** 1268,1273 ****
--- 1318,1324 ----
      if (funcdepth_increment() == FAIL)
        return FAIL;
  
+     CLEAR_FIELD(funclocal);
      CLEAR_FIELD(ectx);
      ectx.ec_dfunc_idx = ufunc->uf_dfunc_idx;
      ga_init2(&ectx.ec_stack, sizeof(typval_T), 500);
***************
*** 1488,1494 ****
                    goto done;
                }
  
!               if (func_return(&ectx) == FAIL)
                    goto failed;
            }
            continue;
--- 1539,1545 ----
                    goto done;
                }
  
!               if (func_return(&funclocal, &ectx) == FAIL)
                    goto failed;
            }
            continue;
***************
*** 2467,2475 ****
            // call a :def function
            case ISN_DCALL:
                SOURCING_LNUM = iptr->isn_lnum;
!               if (call_dfunc(iptr->isn_arg.dfunc.cdf_idx, NULL,
!                             iptr->isn_arg.dfunc.cdf_argcount,
!                             &ectx) == FAIL)
                    goto on_error;
                break;
  
--- 2518,2528 ----
            // call a :def function
            case ISN_DCALL:
                SOURCING_LNUM = iptr->isn_lnum;
!               if (call_dfunc(iptr->isn_arg.dfunc.cdf_idx,
!                               NULL,
!                               iptr->isn_arg.dfunc.cdf_argcount,
!                               &funclocal,
!                               &ectx) == FAIL)
                    goto on_error;
                break;
  
***************
*** 2502,2508 ****
                        partial_tv = *STACK_TV_BOT(0);
                        tv = &partial_tv;
                    }
!                   r = call_partial(tv, pfunc->cpf_argcount, &ectx);
                    if (tv == &partial_tv)
                        clear_tv(&partial_tv);
                    if (r == FAIL)
--- 2555,2562 ----
                        partial_tv = *STACK_TV_BOT(0);
                        tv = &partial_tv;
                    }
!                   r = call_partial(tv, pfunc->cpf_argcount,
!                                                           &funclocal, &ectx);
                    if (tv == &partial_tv)
                        clear_tv(&partial_tv);
                    if (r == FAIL)
***************
*** 2525,2532 ****
                    cufunc_T    *cufunc = &iptr->isn_arg.ufunc;
  
                    SOURCING_LNUM = iptr->isn_lnum;
!                   if (call_eval_func(cufunc->cuf_name,
!                                   cufunc->cuf_argcount, &ectx, iptr) == FAIL)
                        goto on_error;
                }
                break;
--- 2579,2586 ----
                    cufunc_T    *cufunc = &iptr->isn_arg.ufunc;
  
                    SOURCING_LNUM = iptr->isn_lnum;
!                   if (call_eval_func(cufunc->cuf_name, cufunc->cuf_argcount,
!                                             &funclocal, &ectx, iptr) == FAIL)
                        goto on_error;
                }
                break;
***************
*** 2728,2739 ****
                {
                    garray_T    *trystack = &ectx.ec_trystack;
  
!                   if (restore_cmdmod)
                    {
                        cmdmod.cmod_filter_regmatch.regprog = NULL;
                        undo_cmdmod(&cmdmod);
!                       cmdmod = save_cmdmod;
!                       restore_cmdmod = FALSE;
                    }
                    if (trystack->ga_len > 0)
                    {
--- 2782,2793 ----
                {
                    garray_T    *trystack = &ectx.ec_trystack;
  
!                   if (funclocal.floc_restore_cmdmod)
                    {
                        cmdmod.cmod_filter_regmatch.regprog = NULL;
                        undo_cmdmod(&cmdmod);
!                       cmdmod = funclocal.floc_save_cmdmod;
!                       funclocal.floc_restore_cmdmod = FALSE;
                    }
                    if (trystack->ga_len > 0)
                    {
***************
*** 3649,3657 ****
                break;
  
            case ISN_CMDMOD:
!               save_cmdmod = cmdmod;
!               restore_cmdmod = TRUE;
!               restore_cmdmod_stacklen = ectx.ec_stack.ga_len;
                cmdmod = *iptr->isn_arg.cmdmod.cf_cmdmod;
                apply_cmdmod(&cmdmod);
                break;
--- 3703,3711 ----
                break;
  
            case ISN_CMDMOD:
!               funclocal.floc_save_cmdmod = cmdmod;
!               funclocal.floc_restore_cmdmod = TRUE;
!               funclocal.floc_restore_cmdmod_stacklen = ectx.ec_stack.ga_len;
                cmdmod = *iptr->isn_arg.cmdmod.cf_cmdmod;
                apply_cmdmod(&cmdmod);
                break;
***************
*** 3660,3667 ****
                // filter regprog is owned by the instruction, don't free it
                cmdmod.cmod_filter_regmatch.regprog = NULL;
                undo_cmdmod(&cmdmod);
!               cmdmod = save_cmdmod;
!               restore_cmdmod = FALSE;
                break;
  
            case ISN_UNPACK:
--- 3714,3721 ----
                // filter regprog is owned by the instruction, don't free it
                cmdmod.cmod_filter_regmatch.regprog = NULL;
                undo_cmdmod(&cmdmod);
!               cmdmod = funclocal.floc_save_cmdmod;
!               funclocal.floc_restore_cmdmod = FALSE;
                break;
  
            case ISN_UNPACK:
***************
*** 3790,3796 ****
        if (ectx.ec_frame_idx == initial_frame_idx)
            goto done;
  
!       if (func_return(&ectx) == FAIL)
            // only fails when out of memory
            goto failed;
        continue;
--- 3844,3850 ----
        if (ectx.ec_frame_idx == initial_frame_idx)
            goto done;
  
!       if (func_return(&funclocal, &ectx) == FAIL)
            // only fails when out of memory
            goto failed;
        continue;
***************
*** 3805,3813 ****
            // If a sequence of instructions causes an error while ":silent!"
            // was used, restore the stack length and jump ahead to restoring
            // the cmdmod.
!           if (restore_cmdmod)
            {
!               while (ectx.ec_stack.ga_len > restore_cmdmod_stacklen)
                {
                    --ectx.ec_stack.ga_len;
                    clear_tv(STACK_TV_BOT(0));
--- 3859,3868 ----
            // If a sequence of instructions causes an error while ":silent!"
            // was used, restore the stack length and jump ahead to restoring
            // the cmdmod.
!           if (funclocal.floc_restore_cmdmod)
            {
!               while (ectx.ec_stack.ga_len
!                                     > funclocal.floc_restore_cmdmod_stacklen)
                {
                    --ectx.ec_stack.ga_len;
                    clear_tv(STACK_TV_BOT(0));
***************
*** 3834,3840 ****
  failed:
      // When failed need to unwind the call stack.
      while (ectx.ec_frame_idx != initial_frame_idx)
!       func_return(&ectx);
  
      // Deal with any remaining closures, they may be in use somewhere.
      if (ectx.ec_funcrefs.ga_len > 0)
--- 3889,3895 ----
  failed:
      // When failed need to unwind the call stack.
      while (ectx.ec_frame_idx != initial_frame_idx)
!       func_return(&funclocal, &ectx);
  
      // Deal with any remaining closures, they may be in use somewhere.
      if (ectx.ec_funcrefs.ga_len > 0)
***************
*** 3862,3872 ****
      }
      msg_list = saved_msg_list;
  
!     if (restore_cmdmod)
      {
        cmdmod.cmod_filter_regmatch.regprog = NULL;
        undo_cmdmod(&cmdmod);
!       cmdmod = save_cmdmod;
      }
      emsg_silent_def = save_emsg_silent_def;
      did_emsg_def += save_did_emsg_def;
--- 3917,3927 ----
      }
      msg_list = saved_msg_list;
  
!     if (funclocal.floc_restore_cmdmod)
      {
        cmdmod.cmod_filter_regmatch.regprog = NULL;
        undo_cmdmod(&cmdmod);
!       cmdmod = funclocal.floc_save_cmdmod;
      }
      emsg_silent_def = save_emsg_silent_def;
      did_emsg_def += save_did_emsg_def;
*** ../vim-8.2.2649/src/vim9.h  2021-02-21 21:32:38.301201176 +0100
--- src/vim9.h  2021-03-24 21:41:09.265664954 +0100
***************
*** 402,413 ****
  // - ec_dfunc_idx:   function index
  // - ec_iidx:        instruction index
  // - ec_outer:             stack used for closures
  // - ec_frame_idx:   previous frame index
  #define STACK_FRAME_FUNC_OFF 0
  #define STACK_FRAME_IIDX_OFF 1
  #define STACK_FRAME_OUTER_OFF 2
! #define STACK_FRAME_IDX_OFF 3
! #define STACK_FRAME_SIZE 4
  
  
  #ifdef DEFINE_VIM9_GLOBALS
--- 402,415 ----
  // - ec_dfunc_idx:   function index
  // - ec_iidx:        instruction index
  // - ec_outer:             stack used for closures
+ // - funclocal:            function-local data
  // - ec_frame_idx:   previous frame index
  #define STACK_FRAME_FUNC_OFF 0
  #define STACK_FRAME_IIDX_OFF 1
  #define STACK_FRAME_OUTER_OFF 2
! #define STACK_FRAME_FUNCLOCAL_OFF 3
! #define STACK_FRAME_IDX_OFF 4
! #define STACK_FRAME_SIZE 5
  
  
  #ifdef DEFINE_VIM9_GLOBALS
*** ../vim-8.2.2649/src/testdir/test_vim9_func.vim      2021-03-22 
20:48:57.863992154 +0100
--- src/testdir/test_vim9_func.vim      2021-03-24 21:56:22.291344330 +0100
***************
*** 2363,2368 ****
--- 2363,2391 ----
    delete(fname)
  enddef
  
+ def Test_cmdmod_silent_nested()
+   var lines =<< trim END
+       vim9script
+       var result = ''
+ 
+       def Error()
+           result ..= 'Eb'
+           eval [][0]
+           result ..= 'Ea'
+       enddef
+ 
+       def Crash()
+           result ..= 'Cb'
+           sil! Error()
+           result ..= 'Ca'
+       enddef
+ 
+       Crash()
+       assert_equal('CbEbEaCa', result)
+   END
+   CheckScriptSuccess(lines)
+ enddef
+ 
  def Test_dict_member_with_silent()
    var lines =<< trim END
        vim9script
*** ../vim-8.2.2649/src/version.c       2021-03-24 20:08:08.540745895 +0100
--- src/version.c       2021-03-24 21:18:10.087243868 +0100
***************
*** 752,753 ****
--- 752,755 ----
  {   /* Add new patch number below this line */
+ /**/
+     2650,
  /**/

-- 
I have a drinking problem -- I don't have a drink!

 /// Bram Moolenaar -- [email protected] -- http://www.Moolenaar.net   \\\
///                                                                      \\\
\\\        sponsor Vim, vote for features -- http://www.Vim.org/sponsor/ ///
 \\\            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/202103242101.12OL1hn22945940%40masaka.moolenaar.net.

Raspunde prin e-mail lui