]> granicus.if.org Git - vim/commitdiff
patch 9.0.0406: deferred functions not invoked when partial func exits v9.0.0406
authorBram Moolenaar <Bram@vim.org>
Wed, 7 Sep 2022 16:28:09 +0000 (17:28 +0100)
committerBram Moolenaar <Bram@vim.org>
Wed, 7 Sep 2022 16:28:09 +0000 (17:28 +0100)
Problem:    Deferred functions not invoked when partial func exits.
Solution:   Create a funccall_T when calling a :def function.

src/eval.c
src/proto/userfunc.pro
src/testdir/test_user_func.vim
src/userfunc.c
src/version.c

index c4d3781a2d3e5f4eda14acb86ffdf67da3703f9d..98914738139b217ba9c7b001e53bf31a1310a000 100644 (file)
@@ -263,10 +263,17 @@ eval_expr_typval(typval_T *expr, typval_T *argv, int argc, typval_T *rettv)
        if (partial->pt_func != NULL
                          && partial->pt_func->uf_def_status != UF_NOT_COMPILED)
        {
+           funccall_T  *fc = create_funccal(partial->pt_func, rettv);
+           int         r;
+
+           if (fc == NULL)
+               return FAIL;
+
            // Shortcut to call a compiled function without overhead.
-           // FIXME: should create a funccal and link it in current_funccal.
-           if (call_def_function(partial->pt_func, argc, argv,
-                               DEF_USE_PT_ARGV, partial, NULL, rettv) == FAIL)
+           r = call_def_function(partial->pt_func, argc, argv,
+                                         DEF_USE_PT_ARGV, partial, fc, rettv);
+           remove_funccal();
+           if (r == FAIL)
                return FAIL;
        }
        else
index 4fbbe86c9928486f87ef9a7a4e8a9c2c3b5cbd7b..555830ef65c0ac6d03b089098a8f05f4ac4442eb 100644 (file)
@@ -21,6 +21,8 @@ int funcdepth_increment(void);
 void funcdepth_decrement(void);
 int funcdepth_get(void);
 void funcdepth_restore(int depth);
+funccall_T *create_funccal(ufunc_T *fp, typval_T *rettv);
+void remove_funccal(void);
 int check_user_func_argcount(ufunc_T *fp, int argcount);
 int call_user_func_check(ufunc_T *fp, int argcount, typval_T *argvars, typval_T *rettv, funcexe_T *funcexe, dict_T *selfdict);
 void save_funccal(funccal_entry_T *entry);
index 0a942646f8b78be90a7de778e246fd4bd951f4ae..6b17534406934e59660d5bd98f88874757acefac 100644 (file)
@@ -625,6 +625,29 @@ func Test_defer_quitall()
   call assert_false(filereadable('XQuitallTwo'))
 endfunc
 
+func Test_defer_quitall_in_expr_func()
+  let lines =<< trim END
+      def DefIndex(idx: number, val: string): bool
+        call writefile([idx .. ': ' .. val], 'Xentry' .. idx, 'D')
+        if val == 'b'
+          qa!
+        endif
+        return val == 'c'
+      enddef
+
+      def Test_defer_in_funcref()
+        assert_equal(2, indexof(['a', 'b', 'c'], funcref('g:DefIndex')))
+      enddef
+      call Test_defer_in_funcref()
+  END
+  call writefile(lines, 'XdeferQuitallExpr', 'D')
+  let res = system(GetVimCommandClean() .. ' -X -S XdeferQuitallExpr')
+  call assert_equal(0, v:shell_error)
+  call assert_false(filereadable('Xentry0'))
+  call assert_false(filereadable('Xentry1'))
+  call assert_false(filereadable('Xentry2'))
+endfunc
+
 func FuncIndex(idx, val)
   call writefile([a:idx .. ': ' .. a:val], 'Xentry' .. a:idx, 'D')
   return a:val == 'c'
index 19027785bdd2eafd74f10509477dc54e9fe5d634..fea5b7980315535b9bcdb3cc113e76da44ff0ebc 100644 (file)
@@ -2580,6 +2580,40 @@ funcdepth_restore(int depth)
     funcdepth = depth;
 }
 
+/*
+ * Allocate a funccall_T, link it in current_funccal and fill in "fp" and
+ * "rettv".
+ * Must be followed by one call to remove_funccal() or cleanup_function_call().
+ * Returns NULL when allocation fails.
+ */
+    funccall_T *
+create_funccal(ufunc_T *fp, typval_T *rettv)
+{
+    funccall_T *fc = ALLOC_CLEAR_ONE(funccall_T);
+
+    if (fc == NULL)
+       return NULL;
+    fc->fc_caller = current_funccal;
+    current_funccal = fc;
+    fc->fc_func = fp;
+    func_ptr_ref(fp);
+    fc->fc_rettv = rettv;
+    return fc;
+}
+
+/*
+ * To be called when returning from a compiled function; restores
+ * current_funccal.
+ */
+    void
+remove_funccal()
+{
+    funccall_T *fc = current_funccal;
+
+    current_funccal = fc->fc_caller;
+    free_funccal(fc);
+}
+
 /*
  * Call a user function.
  */
@@ -2627,20 +2661,15 @@ call_user_func(
 
     line_breakcheck();         // check for CTRL-C hit
 
-    fc = ALLOC_CLEAR_ONE(funccall_T);
+    fc = create_funccal(fp, rettv);
     if (fc == NULL)
        return;
-    fc->fc_caller = current_funccal;
-    current_funccal = fc;
-    fc->fc_func = fp;
-    fc->fc_rettv = rettv;
     fc->fc_level = ex_nesting_level;
     // Check if this function has a breakpoint.
     fc->fc_breakpoint = dbg_find_breakpoint(FALSE, fp->uf_name, (linenr_T)0);
     fc->fc_dbg_tick = debug_tick;
     // Set up fields for closure.
     ga_init2(&fc->fc_ufuncs, sizeof(ufunc_T *), 1);
-    func_ptr_ref(fp);
 
     if (fp->uf_def_status != UF_NOT_COMPILED)
     {
@@ -2661,8 +2690,7 @@ call_user_func(
                                  || (caller != NULL && caller->uf_profiling)))
            profile_may_end_func(&profile_info, fp, caller);
 #endif
-       current_funccal = fc->fc_caller;
-       free_funccal(fc);
+       remove_funccal();
        sticky_cmdmod_flags = save_sticky_cmdmod_flags;
        return;
     }
index ff5fd12b7bcb9f5f4abde8ddef5556dfa1e250b9..e4b0b09a2dcf083cc30039e2246cf9b7bac86af1 100644 (file)
@@ -703,6 +703,8 @@ static char *(features[]) =
 
 static int included_patches[] =
 {   /* Add new patch number below this line */
+/**/
+    406,
 /**/
     405,
 /**/