]> granicus.if.org Git - vim/commitdiff
patch 8.2.2650: Vim9: command modifiers not handled in nested function v8.2.2650
authorBram Moolenaar <Bram@vim.org>
Wed, 24 Mar 2021 21:00:56 +0000 (22:00 +0100)
committerBram Moolenaar <Bram@vim.org>
Wed, 24 Mar 2021 21:00:56 +0000 (22:00 +0100)
Problem:    Vim9: command modifiers not handled in nested function.
Solution:   Keep function-local info in a structure and save it on the stack.

src/testdir/test_vim9_func.vim
src/version.c
src/vim9.h
src/vim9execute.c

index 275541294e0261e77802bea2cee34b5125fac482..5faae827052ba41152bdbed72611ba388a7ef9a5 100644 (file)
@@ -2363,6 +2363,29 @@ def Test_cmdmod_silent_restored()
   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
index 0e02deb70bd8652c084f80210afac9eca07e4e4e..594f88febe75cbd371d2740732fb2130b8a8f6f7 100644 (file)
@@ -750,6 +750,8 @@ static char *(features[]) =
 
 static int included_patches[] =
 {   /* Add new patch number below this line */
+/**/
+    2650,
 /**/
     2649,
 /**/
index 66a21b77a7991c86aea0fcd70372bea45914a882..a0757fd31f5022ac47298683d2736bf4cc6c086c 100644 (file)
@@ -402,12 +402,14 @@ struct dfunc_S {
 // - 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_IDX_OFF 3
-#define STACK_FRAME_SIZE 4
+#define STACK_FRAME_FUNCLOCAL_OFF 3
+#define STACK_FRAME_IDX_OFF 4
+#define STACK_FRAME_SIZE 5
 
 
 #ifdef DEFINE_VIM9_GLOBALS
index 6b8cd075c10810a48f53f8f51528b27a586378a4..52c4193c6a55e9944e0c36411a395286599d2912 100644 (file)
@@ -154,6 +154,15 @@ exe_newlist(int count, ectx_T *ectx)
     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,16 +179,22 @@ exe_newlist(int count, ectx_T *ectx)
  * - reserved space for local variables
  */
     static int
-call_dfunc(int cdf_idx, partial_T *pt, int argcount_arg, ectx_T *ectx)
+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;
+    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,6 +282,16 @@ call_dfunc(int cdf_idx, partial_T *pt, int argcount_arg, ectx_T *ectx)
     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,6 +305,7 @@ call_dfunc(int cdf_idx, partial_T *pt, int argcount_arg, ectx_T *ectx)
     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,7 +556,7 @@ funcstack_check_refcount(funcstack_T *funcstack)
  * Return from the current function.
  */
     static int
-func_return(ectx_T *ectx)
+func_return(funclocal_T *funclocal, ectx_T *ectx)
 {
     int                idx;
     int                ret_idx;
@@ -543,6 +569,7 @@ func_return(ectx_T *ectx)
                                        + 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,11 +619,21 @@ func_return(ectx_T *ectx)
                                        + 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,6 +727,7 @@ call_ufunc(
        ufunc_T     *ufunc,
        partial_T   *pt,
        int         argcount,
+       funclocal_T *funclocal,
        ectx_T      *ectx,
        isn_T       *iptr)
 {
@@ -729,7 +767,7 @@ call_ufunc(
            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);
+       return call_dfunc(ufunc->uf_dfunc_idx, pt, argcount, funclocal, ectx);
     }
 
     if (call_prepare(argcount, argvars, ectx) == FAIL)
@@ -773,7 +811,12 @@ vim9_aborting(int prev_called_emsg)
  * 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)
+call_by_name(
+       char_u      *name,
+       int         argcount,
+       funclocal_T *funclocal,
+       ectx_T      *ectx,
+       isn_T       *iptr)
 {
     ufunc_T *ufunc;
 
@@ -824,14 +867,18 @@ call_by_name(char_u *name, int argcount, ectx_T *ectx, isn_T *iptr)
            }
        }
 
-       return call_ufunc(ufunc, NULL, argcount, ectx, iptr);
+       return call_ufunc(ufunc, NULL, argcount, funclocal, ectx, iptr);
     }
 
     return FAIL;
 }
 
     static int
-call_partial(typval_T *tv, int argcount_arg, ectx_T *ectx)
+call_partial(
+       typval_T    *tv,
+       int         argcount_arg,
+       funclocal_T *funclocal,
+       ectx_T      *ectx)
 {
     int                argcount = argcount_arg;
     char_u     *name = NULL;
@@ -860,7 +907,7 @@ call_partial(typval_T *tv, int argcount_arg, ectx_T *ectx)
        }
 
        if (pt->pt_func != NULL)
-           return call_ufunc(pt->pt_func, pt, argcount, ectx, NULL);
+           return call_ufunc(pt->pt_func, pt, argcount, funclocal, ectx, NULL);
 
        name = pt->pt_name;
     }
@@ -878,7 +925,7 @@ call_partial(typval_T *tv, int argcount_arg, ectx_T *ectx)
        if (error != FCERR_NONE)
            res = FAIL;
        else
-           res = call_by_name(fname, argcount, ectx, NULL);
+           res = call_by_name(fname, argcount, funclocal, ectx, NULL);
        vim_free(tofree);
     }
 
@@ -1125,12 +1172,17 @@ get_script_svar(scriptref_T *sref, ectx_T *ectx)
  * "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)
+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, ectx, iptr);
+    res = call_by_name(name, argcount, funclocal, ectx, iptr);
     if (res == FAIL && called_emsg == called_emsg_before)
     {
        dictitem_T      *v;
@@ -1146,7 +1198,7 @@ call_eval_func(char_u *name, int argcount, ectx_T *ectx, isn_T *iptr)
            semsg(_(e_unknownfunc), name);
            return FAIL;
        }
-       return call_partial(&v->di_tv, argcount, ectx);
+       return call_partial(&v->di_tv, argcount, funclocal, ectx);
     }
     return res;
 }
@@ -1222,9 +1274,7 @@ call_def_function(
     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;
+    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,6 +1318,7 @@ call_def_function(
     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,7 +1539,7 @@ call_def_function(
                    goto done;
                }
 
-               if (func_return(&ectx) == FAIL)
+               if (func_return(&funclocal, &ectx) == FAIL)
                    goto failed;
            }
            continue;
@@ -2467,9 +2518,11 @@ call_def_function(
            // 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)
+               if (call_dfunc(iptr->isn_arg.dfunc.cdf_idx,
+                               NULL,
+                               iptr->isn_arg.dfunc.cdf_argcount,
+                               &funclocal,
+                               &ectx) == FAIL)
                    goto on_error;
                break;
 
@@ -2502,7 +2555,8 @@ call_def_function(
                        partial_tv = *STACK_TV_BOT(0);
                        tv = &partial_tv;
                    }
-                   r = call_partial(tv, pfunc->cpf_argcount, &ectx);
+                   r = call_partial(tv, pfunc->cpf_argcount,
+                                                           &funclocal, &ectx);
                    if (tv == &partial_tv)
                        clear_tv(&partial_tv);
                    if (r == FAIL)
@@ -2525,8 +2579,8 @@ call_def_function(
                    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)
+                   if (call_eval_func(cufunc->cuf_name, cufunc->cuf_argcount,
+                                             &funclocal, &ectx, iptr) == FAIL)
                        goto on_error;
                }
                break;
@@ -2728,12 +2782,12 @@ call_def_function(
                {
                    garray_T    *trystack = &ectx.ec_trystack;
 
-                   if (restore_cmdmod)
+                   if (funclocal.floc_restore_cmdmod)
                    {
                        cmdmod.cmod_filter_regmatch.regprog = NULL;
                        undo_cmdmod(&cmdmod);
-                       cmdmod = save_cmdmod;
-                       restore_cmdmod = FALSE;
+                       cmdmod = funclocal.floc_save_cmdmod;
+                       funclocal.floc_restore_cmdmod = FALSE;
                    }
                    if (trystack->ga_len > 0)
                    {
@@ -3649,9 +3703,9 @@ call_def_function(
                break;
 
            case ISN_CMDMOD:
-               save_cmdmod = cmdmod;
-               restore_cmdmod = TRUE;
-               restore_cmdmod_stacklen = ectx.ec_stack.ga_len;
+               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,8 +3714,8 @@ call_def_function(
                // 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;
+               cmdmod = funclocal.floc_save_cmdmod;
+               funclocal.floc_restore_cmdmod = FALSE;
                break;
 
            case ISN_UNPACK:
@@ -3790,7 +3844,7 @@ func_return:
        if (ectx.ec_frame_idx == initial_frame_idx)
            goto done;
 
-       if (func_return(&ectx) == FAIL)
+       if (func_return(&funclocal, &ectx) == FAIL)
            // only fails when out of memory
            goto failed;
        continue;
@@ -3805,9 +3859,10 @@ on_error:
            // 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)
+           if (funclocal.floc_restore_cmdmod)
            {
-               while (ectx.ec_stack.ga_len > restore_cmdmod_stacklen)
+               while (ectx.ec_stack.ga_len
+                                     > funclocal.floc_restore_cmdmod_stacklen)
                {
                    --ectx.ec_stack.ga_len;
                    clear_tv(STACK_TV_BOT(0));
@@ -3834,7 +3889,7 @@ done:
 failed:
     // When failed need to unwind the call stack.
     while (ectx.ec_frame_idx != initial_frame_idx)
-       func_return(&ectx);
+       func_return(&funclocal, &ectx);
 
     // Deal with any remaining closures, they may be in use somewhere.
     if (ectx.ec_funcrefs.ga_len > 0)
@@ -3862,11 +3917,11 @@ failed:
     }
     msg_list = saved_msg_list;
 
-    if (restore_cmdmod)
+    if (funclocal.floc_restore_cmdmod)
     {
        cmdmod.cmod_filter_regmatch.regprog = NULL;
        undo_cmdmod(&cmdmod);
-       cmdmod = save_cmdmod;
+       cmdmod = funclocal.floc_save_cmdmod;
     }
     emsg_silent_def = save_emsg_silent_def;
     did_emsg_def += save_did_emsg_def;