0
0
mirror of https://github.com/vim/vim.git synced 2025-09-25 03:54:15 -04:00

patch 9.0.1501: crash with nested :try and :throw in catch block

Problem:    Crash with nested :try and :throw in catch block.
Solution:   Jump to :endtry before returning from function. (closes #12245)
This commit is contained in:
Bram Moolenaar
2023-04-30 18:50:48 +01:00
parent 58a44751ce
commit 3ef2e41128
3 changed files with 69 additions and 7 deletions

View File

@@ -812,6 +812,41 @@ def Test_try_catch_throw()
v9.CheckDefAndScriptSuccess(lines) v9.CheckDefAndScriptSuccess(lines)
enddef 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() def Test_try_var_decl()
var lines =<< trim END var lines =<< trim END
vim9script vim9script

View File

@@ -695,6 +695,8 @@ static char *(features[]) =
static int included_patches[] = static int included_patches[] =
{ /* Add new patch number below this line */ { /* Add new patch number below this line */
/**/
1501,
/**/ /**/
1500, 1500,
/**/ /**/

View File

@@ -3053,6 +3053,16 @@ exec_instructions(ectx_T *ectx)
goto theend; goto theend;
did_throw = TRUE; did_throw = TRUE;
*msg_list = NULL; *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)) if (unlikely(did_throw))
@@ -3066,7 +3076,11 @@ exec_instructions(ectx_T *ectx)
while (index > 0) while (index > 0)
{ {
trycmd = ((trycmd_T *)trystack->ga_data) + index - 1; 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; break;
// In the catch and finally block of this try we have to go up // In the catch and finally block of this try we have to go up
// one level. // one level.
@@ -3076,21 +3090,32 @@ exec_instructions(ectx_T *ectx)
if (trycmd != NULL && trycmd->tcd_frame_idx == ectx->ec_frame_idx) if (trycmd != NULL && trycmd->tcd_frame_idx == ectx->ec_frame_idx)
{ {
if (trycmd->tcd_in_catch) if (trycmd->tcd_in_catch)
{
if (trycmd->tcd_finally_idx > 0)
{ {
// exception inside ":catch", jump to ":finally" once // exception inside ":catch", jump to ":finally" once
ectx->ec_iidx = trycmd->tcd_finally_idx; ectx->ec_iidx = trycmd->tcd_finally_idx;
trycmd->tcd_finally_idx = 0; trycmd->tcd_finally_idx = 0;
} }
else else
{
// exception inside ":catch" or ":finally", jump to
// ":endtry"
ectx->ec_iidx = trycmd->tcd_endtry_idx;
}
}
else
{
// jump to first ":catch" // jump to first ":catch"
ectx->ec_iidx = trycmd->tcd_catch_idx; 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 did_throw = FALSE; // don't come back here until :endtry
trycmd->tcd_did_throw = TRUE; trycmd->tcd_did_throw = TRUE;
} }
else 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. // Push a dummy return value.
if (GA_GROW_FAILS(&ectx->ec_stack, 1)) if (GA_GROW_FAILS(&ectx->ec_stack, 1))
goto theend; goto theend;
@@ -4652,7 +4677,7 @@ exec_instructions(ectx_T *ectx)
if (trystack->ga_len == 0 && trylevel == 0 && emsg_silent) 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. // the function to abort but not display an error.
tv = STACK_TV_BOT(-1); tv = STACK_TV_BOT(-1);
clear_tv(tv); clear_tv(tv);