]> granicus.if.org Git - vim/commitdiff
patch 8.2.2614: Vim9: function is deleted while executing v8.2.2614
authorBram Moolenaar <Bram@vim.org>
Wed, 17 Mar 2021 14:03:04 +0000 (15:03 +0100)
committerBram Moolenaar <Bram@vim.org>
Wed, 17 Mar 2021 14:03:04 +0000 (15:03 +0100)
Problem:    Vim9: function is deleted while executing.
Solution:   increment the call count, when more than zero do not delete the
            function but mark it as dead. (closes #7977)

src/testdir/test_vim9_script.vim
src/userfunc.c
src/version.c
src/vim9execute.c

index c476acf8963ce414f3f6dbe8c209feaa3661bcd6..418435bff2a69785c99999106143c4b99c9e3a79 100644 (file)
@@ -1561,6 +1561,35 @@ def Test_script_reload_change_type()
   delete('Xreload.vim')
 enddef
 
+" Define CallFunc so that the test can be compiled
+command CallFunc echo 'nop'
+
+def Test_script_reload_from_function()
+  var lines =<< trim END
+      vim9script
+
+      if exists('g:loaded')
+        finish
+      endif
+      g:loaded = 1
+      delcommand CallFunc
+      command CallFunc Func()
+      def Func()
+        so /tmp/test.vim
+        g:didTheFunc = 1
+      enddef
+  END
+  writefile(lines, 'XreloadFunc.vim')
+  source XreloadFunc.vim
+  CallFunc
+  assert_equal(1, g:didTheFunc)
+
+  delete('XreloadFunc.vim')
+  delcommand CallFunc
+  unlet g:loaded
+  unlet g:didTheFunc
+enddef
+
 def Test_script_var_shadows_function()
   var lines =<< trim END
       vim9script
index e89b8858d331549b3bc0430eb50bd96c7c263bdf..4793ee555285c7c50c4c7a5c9d688582aaf396f8 100644 (file)
@@ -1359,9 +1359,12 @@ func_remove(ufunc_T *fp)
        // function, so we can find the index when defining the function again.
        // Do remove it when it's a copy.
        if (fp->uf_def_status == UF_COMPILED && (fp->uf_flags & FC_COPY) == 0)
+       {
            fp->uf_flags |= FC_DEAD;
-       else
-           hash_remove(&func_hashtab, hi);
+           return FALSE;
+       }
+       hash_remove(&func_hashtab, hi);
+       fp->uf_flags |= FC_DELETED;
        return TRUE;
     }
     return FALSE;
@@ -2134,11 +2137,23 @@ delete_script_functions(int sid)
                    int changed = func_hashtab.ht_changed;
 
                    fp->uf_flags |= FC_DEAD;
-                   func_clear(fp, TRUE);
-                   // When clearing a function another function can be cleared
-                   // as a side effect.  When that happens start over.
-                   if (changed != func_hashtab.ht_changed)
-                       break;
+
+                   if (fp->uf_calls > 0)
+                   {
+                       // Function is executing, don't free it but do remove
+                       // it from the hashtable.
+                       if (func_remove(fp))
+                           fp->uf_refcount--;
+                   }
+                   else
+                   {
+                       func_clear(fp, TRUE);
+                       // When clearing a function another function can be
+                       // cleared as a side effect.  When that happens start
+                       // over.
+                       if (changed != func_hashtab.ht_changed)
+                           break;
+                   }
                }
                --todo;
            }
@@ -4251,7 +4266,6 @@ ex_delfunction(exarg_T *eap)
                // do remove it from the hashtable.
                if (func_remove(fp))
                    fp->uf_refcount--;
-               fp->uf_flags |= FC_DELETED;
            }
            else
                func_clear_free(fp, FALSE);
index 37774a5f542ccb5a8c3b26819a9e77c44c9ab7c7..49eed325b6f9fdeb51be44b72b398275bd4f492f 100644 (file)
@@ -750,6 +750,8 @@ static char *(features[]) =
 
 static int included_patches[] =
 {   /* Add new patch number below this line */
+/**/
+    2614,
 /**/
     2613,
 /**/
index c0a4874022712ad8c2177fe9424035a4cd03b716..6b8cd075c10810a48f53f8f51528b27a586378a4 100644 (file)
@@ -323,6 +323,8 @@ call_dfunc(int cdf_idx, partial_T *pt, int argcount_arg, ectx_T *ectx)
     else
        ectx->ec_outer = NULL;
 
+    ++ufunc->uf_calls;
+
     // Set execution state to the start of the called function.
     ectx->ec_dfunc_idx = cdf_idx;
     ectx->ec_instr = INSTRUCTIONS(dfunc);
@@ -556,6 +558,9 @@ func_return(ectx_T *ectx)
        }
     }
 #endif
+    // TODO: when is it safe to delete the function when it is no longer used?
+    --dfunc->df_ufunc->uf_calls;
+
     // execution context goes one level up
     entry = estack_pop();
     if (entry != NULL)
@@ -1334,7 +1339,7 @@ call_def_function(
            ++ectx.ec_stack.ga_len;
        }
     if (ufunc->uf_va_name != NULL)
-           ++ectx.ec_stack.ga_len;
+       ++ectx.ec_stack.ga_len;
 
     // Frame pointer points to just after arguments.
     ectx.ec_frame_idx = ectx.ec_stack.ga_len;
@@ -1407,6 +1412,9 @@ call_def_function(
     // Do turn errors into exceptions.
     suppress_errthrow = FALSE;
 
+    // Do not delete the function while executing it.
+    ++ufunc->uf_calls;
+
     // When ":silent!" was used before calling then we still abort the
     // function.  If ":silent!" is used in the function then we don't.
     emsg_silent_def = emsg_silent;
@@ -3838,6 +3846,9 @@ failed:
     estack_pop();
     current_sctx = save_current_sctx;
 
+    // TODO: when is it safe to delete the function if it is no longer used?
+    --ufunc->uf_calls;
+
     if (*msg_list != NULL && saved_msg_list != NULL)
     {
        msglist_T **plist = saved_msg_list;