]> git.ipfire.org Git - thirdparty/vim.git/commitdiff
patch 9.0.1501: crash with nested :try and :throw in catch block v9.0.1501
authorBram Moolenaar <Bram@vim.org>
Sun, 30 Apr 2023 17:50:48 +0000 (18:50 +0100)
committerBram Moolenaar <Bram@vim.org>
Sun, 30 Apr 2023 17:50:48 +0000 (18:50 +0100)
Problem:    Crash with nested :try and :throw in catch block.
Solution:   Jump to :endtry before returning from function. (closes #12245)

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

index 3d5148ecc1bd7f7cbaf15e713fc020b182e924a6..3541aa7421f3fc3f7a37eedf9f4c583352697f9a 100644 (file)
@@ -812,6 +812,41 @@ def Test_try_catch_throw()
   v9.CheckDefAndScriptSuccess(lines)
 enddef
 
+def Test_throw_in_nested_try()
+  var lines =<< trim END
+      vim9script
+
+      def Try(F: func(): void)
+        try
+          F()
+        catch
+        endtry
+      enddef
+
+      class X
+        def F()
+          try
+            throw 'Foobar'
+          catch
+            throw v:exception
+          endtry
+        enddef
+      endclass
+
+      def Test_TryMethod()
+        var x = X.new()
+        Try(() => x.F())
+      enddef
+
+
+      try
+        Test_TryMethod()
+      catch
+      endtry
+  END
+  v9.CheckScriptSuccess(lines)
+enddef
+
 def Test_try_var_decl()
   var lines =<< trim END
       vim9script
index b81b734975edf2af3708e6fbf809e523d15f047b..5934272e9d4c777f512cd9d7af4613151e474d0f 100644 (file)
@@ -695,6 +695,8 @@ static char *(features[]) =
 
 static int included_patches[] =
 {   /* Add new patch number below this line */
+/**/
+    1501,
 /**/
     1500,
 /**/
index 33a2fc6084b01f77c73ebeb187a5645090f2076d..8e27166a6f354175bfdca4a85cf432c43a865fca 100644 (file)
@@ -3053,6 +3053,16 @@ exec_instructions(ectx_T *ectx)
                goto theend;
            did_throw = TRUE;
            *msg_list = NULL;
+
+           // This exception was not caught (yet).
+           garray_T    *trystack = &ectx->ec_trystack;
+           if (trystack->ga_len > 0)
+           {
+               trycmd_T *trycmd = ((trycmd_T *)trystack->ga_data)
+                                                       + trystack->ga_len - 1;
+               if (trycmd->tcd_frame_idx == ectx->ec_frame_idx)
+                   trycmd->tcd_caught = FALSE;
+           }
        }
 
        if (unlikely(did_throw))
@@ -3066,7 +3076,11 @@ exec_instructions(ectx_T *ectx)
            while (index > 0)
            {
                trycmd = ((trycmd_T *)trystack->ga_data) + index - 1;
-               if (!trycmd->tcd_in_catch || trycmd->tcd_finally_idx != 0)
+               // 1. after :try and before :catch - jump to first :catch
+               // 2. in :catch block - jump to :finally
+               // 3. in :catch block and no finally - jump to :endtry
+               if (!trycmd->tcd_in_catch || trycmd->tcd_finally_idx != 0
+                               || trycmd->tcd_frame_idx == ectx->ec_frame_idx)
                    break;
                // In the catch and finally block of this try we have to go up
                // one level.
@@ -3077,20 +3091,31 @@ exec_instructions(ectx_T *ectx)
            {
                if (trycmd->tcd_in_catch)
                {
-                   // exception inside ":catch", jump to ":finally" once
-                   ectx->ec_iidx = trycmd->tcd_finally_idx;
-                   trycmd->tcd_finally_idx = 0;
+                   if (trycmd->tcd_finally_idx > 0)
+                   {
+                       // exception inside ":catch", jump to ":finally" once
+                       ectx->ec_iidx = trycmd->tcd_finally_idx;
+                       trycmd->tcd_finally_idx = 0;
+                   }
+                   else
+                   {
+                       // exception inside ":catch" or ":finally", jump to
+                       // ":endtry"
+                       ectx->ec_iidx = trycmd->tcd_endtry_idx;
+                   }
                }
                else
+               {
                    // jump to first ":catch"
                    ectx->ec_iidx = trycmd->tcd_catch_idx;
-               trycmd->tcd_in_catch = TRUE;
+                   trycmd->tcd_in_catch = TRUE;
+               }
                did_throw = FALSE;  // don't come back here until :endtry
                trycmd->tcd_did_throw = TRUE;
            }
            else
            {
-               // Not inside try or need to return from current functions.
+               // Not inside try or need to return from current function.
                // Push a dummy return value.
                if (GA_GROW_FAILS(&ectx->ec_stack, 1))
                    goto theend;
@@ -4652,7 +4677,7 @@ exec_instructions(ectx_T *ectx)
 
                    if (trystack->ga_len == 0 && trylevel == 0 && emsg_silent)
                    {
-                       // throwing an exception while using "silent!" causes
+                       // Throwing an exception while using "silent!" causes
                        // the function to abort but not display an error.
                        tv = STACK_TV_BOT(-1);
                        clear_tv(tv);