Patch 8.2.2539
Problem: Vim9: return from finally block causes a hang.
Solution: Store both the finally and endtry indexes. (closes #7885)
Files: src/vim9execute.c, src/vim9compile.c, src/vim9.h,
src/testdir/test_vim9_script.vim,
src/testdir/test_vim9_disassemble.vim
*** ../vim-8.2.2538/src/vim9execute.c 2021-02-20 17:03:57.984112613 +0100
--- src/vim9execute.c 2021-02-21 21:15:16.101960728 +0100
***************
*** 26,33 ****
typedef struct {
int tcd_frame_idx; // ec_frame_idx at ISN_TRY
int tcd_stack_len; // size of ectx.ec_stack at ISN_TRY
! int tcd_catch_idx; // instruction of the first catch
! int tcd_finally_idx; // instruction of the finally block or
:endtry
int tcd_caught; // catch block entered
int tcd_cont; // :continue encountered, jump here
int tcd_return; // when TRUE return from end of :finally
--- 26,34 ----
typedef struct {
int tcd_frame_idx; // ec_frame_idx at ISN_TRY
int tcd_stack_len; // size of ectx.ec_stack at ISN_TRY
! int tcd_catch_idx; // instruction of the first :catch or
:finally
! int tcd_finally_idx; // instruction of the :finally block or
zero
! int tcd_endtry_idx; // instruction of the :endtry
int tcd_caught; // catch block entered
int tcd_cont; // :continue encountered, jump here
int tcd_return; // when TRUE return from end of :finally
***************
*** 2517,2526 ****
+ trystack->ga_len - 1;
if (trycmd != NULL
&& trycmd->tcd_frame_idx == ectx.ec_frame_idx
! && ectx.ec_instr[trycmd->tcd_finally_idx]
! .isn_type != ISN_ENDTRY)
{
! // jump to ":finally"
ectx.ec_iidx = trycmd->tcd_finally_idx;
trycmd->tcd_return = TRUE;
}
--- 2518,2526 ----
+ trystack->ga_len - 1;
if (trycmd != NULL
&& trycmd->tcd_frame_idx == ectx.ec_frame_idx
! && trycmd->tcd_finally_idx != 0)
{
! // jump to ":finally" once
ectx.ec_iidx = trycmd->tcd_finally_idx;
trycmd->tcd_return = TRUE;
}
***************
*** 2665,2672 ****
CLEAR_POINTER(trycmd);
trycmd->tcd_frame_idx = ectx.ec_frame_idx;
trycmd->tcd_stack_len = ectx.ec_stack.ga_len;
! trycmd->tcd_catch_idx = iptr->isn_arg.try.try_catch;
! trycmd->tcd_finally_idx = iptr->isn_arg.try.try_finally;
}
break;
--- 2665,2673 ----
CLEAR_POINTER(trycmd);
trycmd->tcd_frame_idx = ectx.ec_frame_idx;
trycmd->tcd_stack_len = ectx.ec_stack.ga_len;
! trycmd->tcd_catch_idx =
iptr->isn_arg.try.try_ref->try_catch;
! trycmd->tcd_finally_idx =
iptr->isn_arg.try.try_ref->try_finally;
! trycmd->tcd_endtry_idx =
iptr->isn_arg.try.try_ref->try_endtry;
}
break;
***************
*** 2731,2743 ****
trycmd = ((trycmd_T *)trystack->ga_data)
+ trystack->ga_len - i;
trycmd->tcd_cont = iidx;
! iidx = trycmd->tcd_finally_idx;
}
// jump to :finally or :endtry of current try statement
ectx.ec_iidx = iidx;
}
break;
// end of ":try" block
case ISN_ENDTRY:
{
--- 2732,2757 ----
trycmd = ((trycmd_T *)trystack->ga_data)
+ trystack->ga_len - i;
trycmd->tcd_cont = iidx;
! iidx = trycmd->tcd_finally_idx == 0
! ? trycmd->tcd_endtry_idx : trycmd->tcd_finally_idx;
}
// jump to :finally or :endtry of current try statement
ectx.ec_iidx = iidx;
}
break;
+ case ISN_FINALLY:
+ {
+ garray_T *trystack = &ectx.ec_trystack;
+ trycmd_T *trycmd = ((trycmd_T *)trystack->ga_data)
+ + trystack->ga_len - 1;
+
+ // Reset the index to avoid a return statement jumps here
+ // again.
+ trycmd->tcd_finally_idx = 0;
+ break;
+ }
+
// end of ":try" block
case ISN_ENDTRY:
{
***************
*** 4348,4358 ****
{
try_T *try = &iptr->isn_arg.try;
! smsg("%4d TRY catch -> %d, %s -> %d", current,
! try->try_catch,
! instr[try->try_finally].isn_type == ISN_ENDTRY
! ? "end" : "finally",
! try->try_finally);
}
break;
case ISN_CATCH:
--- 4362,4378 ----
{
try_T *try = &iptr->isn_arg.try;
! if (try->try_ref->try_finally == 0)
! smsg("%4d TRY catch -> %d, endtry -> %d",
! current,
! try->try_ref->try_catch,
! try->try_ref->try_endtry);
! else
! smsg("%4d TRY catch -> %d, finally -> %d, endtry -> %d",
! current,
! try->try_ref->try_catch,
! try->try_ref->try_finally,
! try->try_ref->try_endtry);
}
break;
case ISN_CATCH:
***************
*** 4369,4374 ****
--- 4389,4397 ----
trycont->tct_where);
}
break;
+ case ISN_FINALLY:
+ smsg("%4d FINALLY", current);
+ break;
case ISN_ENDTRY:
smsg("%4d ENDTRY", current);
break;
*** ../vim-8.2.2538/src/vim9compile.c 2021-02-20 17:03:57.984112613 +0100
--- src/vim9compile.c 2021-02-21 21:11:13.815292347 +0100
***************
*** 7518,7527 ****
if (cctx->ctx_skip != SKIP_YES)
{
! // "catch" is set when the first ":catch" is found.
! // "finally" is set when ":finally" or ":endtry" is found
try_scope->se_u.se_try.ts_try_label = instr->ga_len;
! if (generate_instr(cctx, ISN_TRY) == NULL)
return NULL;
}
--- 7518,7534 ----
if (cctx->ctx_skip != SKIP_YES)
{
! isn_T *isn;
!
! // "try_catch" is set when the first ":catch" is found or when no catch
! // is found and ":finally" is found.
! // "try_finally" is set when ":finally" is found
! // "try_endtry" is set when ":endtry" is found
try_scope->se_u.se_try.ts_try_label = instr->ga_len;
! if ((isn = generate_instr(cctx, ISN_TRY)) == NULL)
! return NULL;
! isn->isn_arg.try.try_ref = ALLOC_CLEAR_ONE(tryref_T);
! if (isn->isn_arg.try.try_ref == NULL)
return NULL;
}
***************
*** 7577,7584 ****
// End :try or :catch scope: set value in ISN_TRY instruction
isn = ((isn_T *)instr->ga_data) + scope->se_u.se_try.ts_try_label;
! if (isn->isn_arg.try.try_catch == 0)
! isn->isn_arg.try.try_catch = instr->ga_len;
if (scope->se_u.se_try.ts_catch_label != 0)
{
// Previous catch without match jumps here
--- 7584,7591 ----
// End :try or :catch scope: set value in ISN_TRY instruction
isn = ((isn_T *)instr->ga_data) + scope->se_u.se_try.ts_try_label;
! if (isn->isn_arg.try.try_ref->try_catch == 0)
! isn->isn_arg.try.try_ref->try_catch = instr->ga_len;
if (scope->se_u.se_try.ts_catch_label != 0)
{
// Previous catch without match jumps here
***************
*** 7670,7676 ****
// End :catch or :finally scope: set value in ISN_TRY instruction
isn = ((isn_T *)instr->ga_data) + scope->se_u.se_try.ts_try_label;
! if (isn->isn_arg.try.try_finally != 0)
{
emsg(_(e_finally_dup));
return NULL;
--- 7677,7683 ----
// End :catch or :finally scope: set value in ISN_TRY instruction
isn = ((isn_T *)instr->ga_data) + scope->se_u.se_try.ts_try_label;
! if (isn->isn_arg.try.try_ref->try_finally != 0)
{
emsg(_(e_finally_dup));
return NULL;
***************
*** 7688,7694 ****
compile_fill_jump_to_end(&scope->se_u.se_try.ts_end_label,
this_instr, cctx);
! isn->isn_arg.try.try_finally = this_instr;
if (scope->se_u.se_try.ts_catch_label != 0)
{
// Previous catch without match jumps here
--- 7695,7704 ----
compile_fill_jump_to_end(&scope->se_u.se_try.ts_end_label,
this_instr, cctx);
! // If there is no :catch then an exception jumps to :finally.
! if (isn->isn_arg.try.try_ref->try_catch == 0)
! isn->isn_arg.try.try_ref->try_catch = this_instr;
! isn->isn_arg.try.try_ref->try_finally = this_instr;
if (scope->se_u.se_try.ts_catch_label != 0)
{
// Previous catch without match jumps here
***************
*** 7696,7701 ****
--- 7706,7713 ----
isn->isn_arg.jump.jump_where = this_instr;
scope->se_u.se_try.ts_catch_label = 0;
}
+ if (generate_instr(cctx, ISN_FINALLY) == NULL)
+ return NULL;
// TODO: set index in ts_finally_label jumps
***************
*** 7731,7738 ****
try_isn = ((isn_T *)instr->ga_data) + scope->se_u.se_try.ts_try_label;
if (cctx->ctx_skip != SKIP_YES)
{
! if (try_isn->isn_arg.try.try_catch == 0
! && try_isn->isn_arg.try.try_finally == 0)
{
emsg(_(e_missing_catch_or_finally));
return NULL;
--- 7743,7750 ----
try_isn = ((isn_T *)instr->ga_data) + scope->se_u.se_try.ts_try_label;
if (cctx->ctx_skip != SKIP_YES)
{
! if (try_isn->isn_arg.try.try_ref->try_catch == 0
! &&
try_isn->isn_arg.try.try_ref->try_finally == 0)
{
emsg(_(e_missing_catch_or_finally));
return NULL;
***************
*** 7751,7762 ****
compile_fill_jump_to_end(&scope->se_u.se_try.ts_end_label,
instr->ga_len, cctx);
- // End :catch or :finally scope: set value in ISN_TRY instruction
- if (try_isn->isn_arg.try.try_catch == 0)
- try_isn->isn_arg.try.try_catch = instr->ga_len;
- if (try_isn->isn_arg.try.try_finally == 0)
- try_isn->isn_arg.try.try_finally = instr->ga_len;
-
if (scope->se_u.se_try.ts_catch_label != 0)
{
// Last catch without match jumps here
--- 7763,7768 ----
***************
*** 7770,7780 ****
if (cctx->ctx_skip != SKIP_YES)
{
! if (try_isn->isn_arg.try.try_finally == 0)
! // No :finally encountered, use the try_finaly field to point to
! // ENDTRY, so that TRYCONT can jump there.
! try_isn->isn_arg.try.try_finally = instr->ga_len;
!
if (cctx->ctx_skip != SKIP_YES
&& generate_instr(cctx, ISN_ENDTRY) == NULL)
return NULL;
--- 7776,7784 ----
if (cctx->ctx_skip != SKIP_YES)
{
! // End :catch or :finally scope: set instruction index in ISN_TRY
! // instruction
! try_isn->isn_arg.try.try_ref->try_endtry = instr->ga_len;
if (cctx->ctx_skip != SKIP_YES
&& generate_instr(cctx, ISN_ENDTRY) == NULL)
return NULL;
***************
*** 8867,8872 ****
--- 8871,8880 ----
vim_free(isn->isn_arg.script.scriptref);
break;
+ case ISN_TRY:
+ vim_free(isn->isn_arg.try.try_ref);
+ break;
+
case ISN_2BOOL:
case ISN_2STRING:
case ISN_2STRING_ANY:
***************
*** 8899,8904 ****
--- 8907,8913 ----
case ISN_ENDTRY:
case ISN_EXECCONCAT:
case ISN_EXECUTE:
+ case ISN_FINALLY:
case ISN_FOR:
case ISN_GETITEM:
case ISN_JUMP:
***************
*** 8943,8949 ****
case ISN_STRINDEX:
case ISN_STRSLICE:
case ISN_THROW:
- case ISN_TRY:
case ISN_TRYCONT:
case ISN_UNLETINDEX:
case ISN_UNLETRANGE:
--- 8952,8957 ----
*** ../vim-8.2.2538/src/vim9.h 2021-02-20 17:03:57.984112613 +0100
--- src/vim9.h 2021-02-21 21:08:58.843799050 +0100
***************
*** 100,105 ****
--- 100,106 ----
ISN_THROW, // pop value of stack, store in v:exception
ISN_PUSHEXC, // push v:exception
ISN_CATCH, // drop v:exception
+ ISN_FINALLY, // start of :finally block
ISN_ENDTRY, // take entry off from ec_trystack
ISN_TRYCONT, // handle :continue inside a :try statement
***************
*** 208,217 ****
int for_end; // position to jump to after done
} forloop_T;
! // arguments to ISN_TRY
typedef struct {
int try_catch; // position to jump to on throw
int try_finally; // :finally or :endtry position to jump to
} try_T;
// arguments to ISN_TRYCONT
--- 209,224 ----
int for_end; // position to jump to after done
} forloop_T;
! // indirect arguments to ISN_TRY
typedef struct {
int try_catch; // position to jump to on throw
int try_finally; // :finally or :endtry position to jump to
+ int try_endtry; // :endtry position to jump to
+ } tryref_T;
+
+ // arguments to ISN_TRY
+ typedef struct {
+ tryref_T *try_ref;
} try_T;
// arguments to ISN_TRYCONT
*** ../vim-8.2.2538/src/testdir/test_vim9_script.vim 2021-02-20
08:16:33.823363221 +0100
--- src/testdir/test_vim9_script.vim 2021-02-21 21:20:29.772406658 +0100
***************
*** 577,582 ****
--- 577,592 ----
counter += 1
endfor
assert_equal(4, counter)
+
+ # return in finally after empty catch
+ def ReturnInFinally(): number
+ try
+ finally
+ return 4
+ endtry
+ return 2
+ enddef
+ assert_equal(4, ReturnInFinally())
enddef
def Test_cnext_works_in_catch()
*** ../vim-8.2.2538/src/testdir/test_vim9_disassemble.vim 2021-02-13
15:02:43.063505534 +0100
--- src/testdir/test_vim9_disassemble.vim 2021-02-21 21:26:40.846730718
+0100
***************
*** 422,428 ****
var res = execute('disass s:ScriptFuncTry')
assert_match('<SNR>\d*_ScriptFuncTry\_s*' ..
'try\_s*' ..
! '\d TRY catch -> \d\+, finally -> \d\+\_s*' ..
'echo "yes"\_s*' ..
'\d PUSHS "yes"\_s*' ..
'\d ECHO 1\_s*' ..
--- 422,428 ----
var res = execute('disass s:ScriptFuncTry')
assert_match('<SNR>\d*_ScriptFuncTry\_s*' ..
'try\_s*' ..
! '\d TRY catch -> \d\+, finally -> \d\+, endtry -> \d\+\_s*' ..
'echo "yes"\_s*' ..
'\d PUSHS "yes"\_s*' ..
'\d ECHO 1\_s*' ..
***************
*** 437,442 ****
--- 437,443 ----
'\d\+ PUSHS "no"\_s*' ..
'\d\+ ECHO 1\_s*' ..
'finally\_s*' ..
+ '\d\+ FINALLY\_s*' ..
'throw "end"\_s*' ..
'\d\+ PUSHS "end"\_s*' ..
'\d\+ THROW\_s*' ..
***************
*** 1137,1148 ****
'4 FOR $0 -> 22\_s*' ..
'5 STORE $1\_s*' ..
'try\_s*' ..
! '6 TRY catch -> 17, end -> 20\_s*' ..
'echo "ok"\_s*' ..
'7 PUSHS "ok"\_s*' ..
'8 ECHO 1\_s*' ..
'try\_s*' ..
! '9 TRY catch -> 13, end -> 15\_s*' ..
'echo "deeper"\_s*' ..
'10 PUSHS "deeper"\_s*' ..
'11 ECHO 1\_s*' ..
--- 1138,1149 ----
'4 FOR $0 -> 22\_s*' ..
'5 STORE $1\_s*' ..
'try\_s*' ..
! '6 TRY catch -> 17, endtry -> 20\_s*' ..
'echo "ok"\_s*' ..
'7 PUSHS "ok"\_s*' ..
'8 ECHO 1\_s*' ..
'try\_s*' ..
! '9 TRY catch -> 13, endtry -> 15\_s*' ..
'echo "deeper"\_s*' ..
'10 PUSHS "deeper"\_s*' ..
'11 ECHO 1\_s*' ..
*** ../vim-8.2.2538/src/version.c 2021-02-21 19:12:43.018019657 +0100
--- src/version.c 2021-02-21 21:02:30.801255811 +0100
***************
*** 752,753 ****
--- 752,755 ----
{ /* Add new patch number below this line */
+ /**/
+ 2539,
/**/
--
ARTHUR: Go on, Bors, chop its head off.
BORS: Right. Silly little bleeder. One rabbit stew coming up.
"Monty Python and the Holy Grail" PYTHON (MONTY) PICTURES LTD
/// Bram Moolenaar -- [email protected] -- http://www.Moolenaar.net \\\
/// sponsor Vim, vote for features -- http://www.Vim.org/sponsor/ \\\
\\\ an exciting new programming language -- http://www.Zimbu.org ///
\\\ 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/202102212034.11LKY0Vk3284631%40masaka.moolenaar.net.