Patch 9.0.0397
Problem:    :defer not tested with exceptions and ":qa!".
Solution:   Test :defer works when exceptions are thrown and when ":qa!" is
            used.  Invoke the deferred calls on exit.
Files:      src/main.c, src/userfunc.c, src/proto/userfunc.pro,
            src/vim9execute.c, src/proto/vim9execute.pro, src/structs.h,
            src/eval.c, src/testdir/test_user_func.vim


*** ../vim-9.0.0396/src/main.c  2022-08-29 15:06:46.720715534 +0100
--- src/main.c  2022-09-06 17:42:25.668782607 +0100
***************
*** 1583,1588 ****
--- 1583,1593 ----
      if (!is_not_a_term_or_gui())
        windgoto((int)Rows - 1, 0);
  
+ #ifdef FEAT_EVAL
+     // Invoked all deferred functions in the function stack.
+     invoke_all_defer();
+ #endif
+ 
  #if defined(FEAT_EVAL) || defined(FEAT_SYN_HL)
      // Optionally print hashtable efficiency.
      hash_debug_results();
*** ../vim-9.0.0396/src/userfunc.c      2022-09-05 21:21:21.135941382 +0100
--- src/userfunc.c      2022-09-06 18:23:08.627100776 +0100
***************
*** 33,38 ****
--- 33,39 ----
  static void func_clear(ufunc_T *fp, int force);
  static int func_free(ufunc_T *fp, int force);
  static char_u *untrans_function_name(char_u *name);
+ static void handle_defer_one(funccall_T *funccal);
  
      void
  func_init()
***************
*** 2651,2657 ****
            profile_may_start_func(&profile_info, fp, caller);
  #endif
        sticky_cmdmod_flags = 0;
!       call_def_function(fp, argcount, argvars, funcexe->fe_partial, rettv);
        funcdepth_decrement();
  #ifdef FEAT_PROFILE
        if (do_profiling == PROF_YES && (fp->uf_profiling
--- 2652,2659 ----
            profile_may_start_func(&profile_info, fp, caller);
  #endif
        sticky_cmdmod_flags = 0;
!       call_def_function(fp, argcount, argvars, funcexe->fe_partial,
!                                                                   fc, rettv);
        funcdepth_decrement();
  #ifdef FEAT_PROFILE
        if (do_profiling == PROF_YES && (fp->uf_profiling
***************
*** 2906,2912 ****
                                     DOCMD_NOWAIT|DOCMD_VERBOSE|DOCMD_REPEAT);
  
      // Invoke functions added with ":defer".
!     handle_defer();
  
      --RedrawingDisabled;
  
--- 2908,2914 ----
                                     DOCMD_NOWAIT|DOCMD_VERBOSE|DOCMD_REPEAT);
  
      // Invoke functions added with ":defer".
!     handle_defer_one(current_funccal);
  
      --RedrawingDisabled;
  
***************
*** 5660,5675 ****
  /*
   * Invoked after a functions has finished: invoke ":defer" functions.
   */
!     void
! handle_defer(void)
  {
      int           idx;
  
!     for (idx = current_funccal->fc_defer.ga_len - 1; idx >= 0; --idx)
      {
        funcexe_T   funcexe;
        typval_T    rettv;
!       defer_T     *dr = ((defer_T *)current_funccal->fc_defer.ga_data) + idx;
        int         i;
  
        CLEAR_FIELD(funcexe);
--- 5662,5677 ----
  /*
   * Invoked after a functions has finished: invoke ":defer" functions.
   */
!     static void
! handle_defer_one(funccall_T *funccal)
  {
      int           idx;
  
!     for (idx = funccal->fc_defer.ga_len - 1; idx >= 0; --idx)
      {
        funcexe_T   funcexe;
        typval_T    rettv;
!       defer_T     *dr = ((defer_T *)funccal->fc_defer.ga_data) + idx;
        int         i;
  
        CLEAR_FIELD(funcexe);
***************
*** 5683,5689 ****
        for (i = dr->dr_argcount - 1; i >= 0; --i)
            clear_tv(&dr->dr_argvars[i]);
      }
!     ga_clear(&current_funccal->fc_defer);
  }
  
  /*
--- 5685,5713 ----
        for (i = dr->dr_argcount - 1; i >= 0; --i)
            clear_tv(&dr->dr_argvars[i]);
      }
!     ga_clear(&funccal->fc_defer);
! }
! 
! /*
!  * Called when exiting: call all defer functions.
!  */
!     void
! invoke_all_defer(void)
! {
!     funccall_T *funccal;
! 
!     for (funccal = current_funccal; funccal != NULL; funccal = 
funccal->caller)
!       if (funccal->fc_ectx != NULL)
!       {
!           // :def function
!           unwind_def_callstack(funccal->fc_ectx);
!           may_invoke_defer_funcs(funccal->fc_ectx);
!       }
!       else
!       {
!           // legacy function
!           handle_defer_one(funccal);
!       }
  }
  
  /*
*** ../vim-9.0.0396/src/proto/userfunc.pro      2022-09-04 15:40:31.816188110 
+0100
--- src/proto/userfunc.pro      2022-09-06 17:43:17.560601478 +0100
***************
*** 59,65 ****
  void func_ptr_ref(ufunc_T *fp);
  void ex_return(exarg_T *eap);
  int add_defer(char_u *name, int argcount_arg, typval_T *argvars);
! void handle_defer(void);
  void ex_call(exarg_T *eap);
  int do_return(exarg_T *eap, int reanimate, int is_cmd, void *rettv);
  void discard_pending_return(void *rettv);
--- 59,65 ----
  void func_ptr_ref(ufunc_T *fp);
  void ex_return(exarg_T *eap);
  int add_defer(char_u *name, int argcount_arg, typval_T *argvars);
! void invoke_all_defer(void);
  void ex_call(exarg_T *eap);
  int do_return(exarg_T *eap, int reanimate, int is_cmd, void *rettv);
  void discard_pending_return(void *rettv);
*** ../vim-9.0.0396/src/vim9execute.c   2022-09-05 10:47:10.536279263 +0100
--- src/vim9execute.c   2022-09-06 18:26:04.254681624 +0100
***************
*** 5171,5183 ****
  done:
      ret = OK;
  theend:
!     {
!       dfunc_T *dfunc = ((dfunc_T *)def_functions.ga_data)
!                                                         + ectx->ec_dfunc_idx;
! 
!       if (dfunc->df_defer_var_idx > 0)
!           invoke_defer_funcs(ectx);
!     }
  
      dict_stack_clear(dict_stack_len_at_start);
      ectx->ec_trylevel_at_start = save_trylevel_at_start;
--- 5171,5177 ----
  done:
      ret = OK;
  theend:
!     may_invoke_defer_funcs(ectx);
  
      dict_stack_clear(dict_stack_len_at_start);
      ectx->ec_trylevel_at_start = save_trylevel_at_start;
***************
*** 5258,5263 ****
--- 5252,5258 ----
      int               argc_arg,       // nr of arguments
      typval_T  *argv,          // arguments
      partial_T *partial,       // optional partial for context
+     funccall_T        *funccal,
      typval_T  *rettv)         // return value
  {
      ectx_T    ectx;           // execution context
***************
*** 5494,5499 ****
--- 5489,5498 ----
        ectx.ec_instr = INSTRUCTIONS(dfunc);
      }
  
+     // Store the execution context in funccal, used by invoke_all_defer().
+     if (funccal != NULL)
+       funccal->fc_ectx = &ectx;
+ 
      // Following errors are in the function, not the caller.
      // Commands behave like vim9script.
      estack_push_ufunc(ufunc, 1);
***************
*** 5537,5544 ****
      }
  
      // When failed need to unwind the call stack.
!     while (ectx.ec_frame_idx != ectx.ec_initial_frame_idx)
!       func_return(&ectx);
  
      // Deal with any remaining closures, they may be in use somewhere.
      if (ectx.ec_funcrefs.ga_len > 0)
--- 5536,5542 ----
      }
  
      // When failed need to unwind the call stack.
!     unwind_def_callstack(&ectx);
  
      // Deal with any remaining closures, they may be in use somewhere.
      if (ectx.ec_funcrefs.ga_len > 0)
***************
*** 5604,5609 ****
--- 5602,5631 ----
  }
  
  /*
+  * Called when a def function has finished (possibly failed).
+  * Invoke all the function returns to clean up and invoke deferred functions,
+  * except the toplevel one.
+  */
+     void
+ unwind_def_callstack(ectx_T *ectx)
+ {
+     while (ectx->ec_frame_idx != ectx->ec_initial_frame_idx)
+       func_return(ectx);
+ }
+ 
+ /*
+  * Invoke any deffered functions for the top function in "ectx".
+  */
+     void
+ may_invoke_defer_funcs(ectx_T *ectx)
+ {
+     dfunc_T *dfunc = ((dfunc_T *)def_functions.ga_data) + ectx->ec_dfunc_idx;
+ 
+     if (dfunc->df_defer_var_idx > 0)
+       invoke_defer_funcs(ectx);
+ }
+ 
+ /*
   * List instructions "instr" up to "instr_count" or until ISN_FINISH.
   * "ufunc" has the source lines, NULL for the instructions of ISN_SUBSTITUTE.
   * "pfx" is prefixed to every line.
*** ../vim-9.0.0396/src/proto/vim9execute.pro   2022-09-04 15:40:31.816188110 
+0100
--- src/proto/vim9execute.pro   2022-09-06 18:20:03.231539841 +0100
***************
*** 13,19 ****
  int may_break_in_function(ufunc_T *ufunc);
  int exe_typval_instr(typval_T *tv, typval_T *rettv);
  char_u *exe_substitute_instr(void);
! int call_def_function(ufunc_T *ufunc, int argc_arg, typval_T *argv, partial_T 
*partial, typval_T *rettv);
  void set_context_in_disassemble_cmd(expand_T *xp, char_u *arg);
  char_u *get_disassemble_argument(expand_T *xp, int idx);
  void ex_disassemble(exarg_T *eap);
--- 13,21 ----
  int may_break_in_function(ufunc_T *ufunc);
  int exe_typval_instr(typval_T *tv, typval_T *rettv);
  char_u *exe_substitute_instr(void);
! int call_def_function(ufunc_T *ufunc, int argc_arg, typval_T *argv, partial_T 
*partial, funccall_T *funccal, typval_T *rettv);
! void unwind_def_callstack(ectx_T *ectx);
! void may_invoke_defer_funcs(ectx_T *ectx);
  void set_context_in_disassemble_cmd(expand_T *xp, char_u *arg);
  char_u *get_disassemble_argument(expand_T *xp, int idx);
  void ex_disassemble(exarg_T *eap);
*** ../vim-9.0.0396/src/structs.h       2022-09-03 21:35:50.184158219 +0100
--- src/structs.h       2022-09-06 18:00:22.966072887 +0100
***************
*** 1753,1759 ****
--- 1753,1763 ----
      linenr_T  breakpoint;     // next line with breakpoint or zero
      int               dbg_tick;       // debug_tick when breakpoint was set
      int               level;          // top nesting level of executed 
function
+ 
      garray_T  fc_defer;       // functions to be called on return
+     ectx_T    *fc_ectx;       // execution context for :def function, NULL
+                               // otherwise
+ 
  #ifdef FEAT_PROFILE
      proftime_T        prof_child;     // time spent in a child
  #endif
*** ../vim-9.0.0396/src/eval.c  2022-09-03 21:53:24.623089721 +0100
--- src/eval.c  2022-09-06 18:09:06.693038108 +0100
***************
*** 263,270 ****
        if (partial->pt_func != NULL
                          && partial->pt_func->uf_def_status != UF_NOT_COMPILED)
        {
            if (call_def_function(partial->pt_func, argc, argv,
!                                                      partial, rettv) == FAIL)
                return FAIL;
        }
        else
--- 263,271 ----
        if (partial->pt_func != NULL
                          && partial->pt_func->uf_def_status != UF_NOT_COMPILED)
        {
+           // FIXME: should create a funccal and link it in current_funccal.
            if (call_def_function(partial->pt_func, argc, argv,
!                                                partial, NULL, rettv) == FAIL)
                return FAIL;
        }
        else
*** ../vim-9.0.0396/src/testdir/test_user_func.vim      2022-09-05 
21:21:21.135941382 +0100
--- src/testdir/test_user_func.vim      2022-09-06 17:24:39.414887963 +0100
***************
*** 581,585 ****
--- 581,629 ----
    call assert_fails('defer Part("arg2")', 'E1300:')
  endfunc
  
+ func DeferLevelTwo()
+   call writefile(['text'], 'XDeleteTwo', 'D')
+   throw 'someerror'
+ endfunc
+ 
+ def DeferLevelOne()
+   call writefile(['text'], 'XDeleteOne', 'D')
+   call g:DeferLevelTwo()
+ enddef
+ 
+ func Test_defer_throw()
+   let caught = 'no'
+   try
+     call DeferLevelOne()
+   catch /someerror/
+     let caught = 'yes'
+   endtry
+   call assert_equal('yes', caught)
+   call assert_false(filereadable('XDeleteOne'))
+   call assert_false(filereadable('XDeleteTwo'))
+ endfunc
+ 
+ func Test_defer_quitall()
+   let lines =<< trim END
+       vim9script
+       func DeferLevelTwo()
+         call writefile(['text'], 'XQuitallTwo', 'D')
+         qa!
+       endfunc
+ 
+       def DeferLevelOne()
+         call writefile(['text'], 'XQuitallOne', 'D')
+         call DeferLevelTwo()
+       enddef
+ 
+       DeferLevelOne()
+   END
+   call writefile(lines, 'XdeferQuitall', 'D')
+   let res = system(GetVimCommandClean() .. ' -X -S XdeferQuitall')
+   call assert_equal(0, v:shell_error)
+   call assert_false(filereadable('XQuitallOne'))
+   call assert_false(filereadable('XQuitallTwo'))
+ endfunc
+ 
  
  " vim: shiftwidth=2 sts=2 expandtab
*** ../vim-9.0.0396/src/version.c       2022-09-06 17:00:11.351047779 +0100
--- src/version.c       2022-09-06 17:20:08.182247054 +0100
***************
*** 705,706 ****
--- 705,708 ----
  {   /* Add new patch number below this line */
+ /**/
+     397,
  /**/

-- 
hundred-and-one symptoms of being an internet addict:
17. You turn on your intercom when leaving the room so you can hear if new
    e-mail arrives.

 /// 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/20220906173157.9D6491C0CE4%40moolenaar.net.

Raspunde prin e-mail lui