mirror of
https://github.com/vim/vim.git
synced 2025-09-25 03:54:15 -04:00
patch 8.2.2506: Vim9: :continue does not work correctly in a :try block
Problem: Vim9: :continue does not work correctly in a :try block Solution: Add the TRYCLEANUP instruction. (closes #7827)
This commit is contained in:
@@ -1111,6 +1111,63 @@ def Test_disassemble_for_loop_unpack()
|
|||||||
instr)
|
instr)
|
||||||
enddef
|
enddef
|
||||||
|
|
||||||
|
def ForLoopContinue()
|
||||||
|
for nr in [1, 2]
|
||||||
|
try
|
||||||
|
echo "ok"
|
||||||
|
try
|
||||||
|
echo "deeper"
|
||||||
|
catch
|
||||||
|
continue
|
||||||
|
endtry
|
||||||
|
catch
|
||||||
|
echo "not ok"
|
||||||
|
endtry
|
||||||
|
endfor
|
||||||
|
enddef
|
||||||
|
|
||||||
|
def Test_disassemble_for_loop_continue()
|
||||||
|
var instr = execute('disassemble ForLoopContinue')
|
||||||
|
assert_match('ForLoopContinue\_s*' ..
|
||||||
|
'for nr in \[1, 2]\_s*' ..
|
||||||
|
'0 STORE -1 in $0\_s*' ..
|
||||||
|
'1 PUSHNR 1\_s*' ..
|
||||||
|
'2 PUSHNR 2\_s*' ..
|
||||||
|
'3 NEWLIST size 2\_s*' ..
|
||||||
|
'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*' ..
|
||||||
|
'catch\_s*' ..
|
||||||
|
'12 JUMP -> 15\_s*' ..
|
||||||
|
'13 CATCH\_s*' ..
|
||||||
|
'continue\_s*' ..
|
||||||
|
'14 TRY-CONTINUE 2 levels -> 4\_s*' ..
|
||||||
|
'endtry\_s*' ..
|
||||||
|
'15 ENDTRY\_s*' ..
|
||||||
|
'catch\_s*' ..
|
||||||
|
'16 JUMP -> 20\_s*' ..
|
||||||
|
'17 CATCH\_s*' ..
|
||||||
|
'echo "not ok"\_s*' ..
|
||||||
|
'18 PUSHS "not ok"\_s*' ..
|
||||||
|
'19 ECHO 1\_s*' ..
|
||||||
|
'endtry\_s*' ..
|
||||||
|
'20 ENDTRY\_s*' ..
|
||||||
|
'endfor\_s*' ..
|
||||||
|
'21 JUMP -> 4\_s*' ..
|
||||||
|
'\d\+ DROP\_s*' ..
|
||||||
|
'\d\+ RETURN 0',
|
||||||
|
instr)
|
||||||
|
enddef
|
||||||
|
|
||||||
let g:number = 42
|
let g:number = 42
|
||||||
|
|
||||||
def TypeCast()
|
def TypeCast()
|
||||||
|
@@ -2201,6 +2201,23 @@ def Test_for_loop_unpack()
|
|||||||
CheckDefExecFailure(lines, 'E1017:', 1)
|
CheckDefExecFailure(lines, 'E1017:', 1)
|
||||||
enddef
|
enddef
|
||||||
|
|
||||||
|
def Test_for_loop_with_try_continue()
|
||||||
|
var looped = 0
|
||||||
|
var cleanup = 0
|
||||||
|
for i in range(3)
|
||||||
|
looped += 1
|
||||||
|
try
|
||||||
|
eval [][0]
|
||||||
|
catch
|
||||||
|
continue
|
||||||
|
finally
|
||||||
|
cleanup += 1
|
||||||
|
endtry
|
||||||
|
endfor
|
||||||
|
assert_equal(3, looped)
|
||||||
|
assert_equal(3, cleanup)
|
||||||
|
enddef
|
||||||
|
|
||||||
def Test_while_loop()
|
def Test_while_loop()
|
||||||
var result = ''
|
var result = ''
|
||||||
var cnt = 0
|
var cnt = 0
|
||||||
|
@@ -750,6 +750,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 */
|
||||||
|
/**/
|
||||||
|
2506,
|
||||||
/**/
|
/**/
|
||||||
2505,
|
2505,
|
||||||
/**/
|
/**/
|
||||||
|
10
src/vim9.h
10
src/vim9.h
@@ -100,6 +100,7 @@ typedef enum {
|
|||||||
ISN_PUSHEXC, // push v:exception
|
ISN_PUSHEXC, // push v:exception
|
||||||
ISN_CATCH, // drop v:exception
|
ISN_CATCH, // drop v:exception
|
||||||
ISN_ENDTRY, // take entry off from ec_trystack
|
ISN_ENDTRY, // take entry off from ec_trystack
|
||||||
|
ISN_TRYCONT, // handle :continue inside a :try statement
|
||||||
|
|
||||||
// more expression operations
|
// more expression operations
|
||||||
ISN_ADDLIST, // add two lists
|
ISN_ADDLIST, // add two lists
|
||||||
@@ -209,9 +210,15 @@ typedef struct {
|
|||||||
// arguments to ISN_TRY
|
// arguments to ISN_TRY
|
||||||
typedef struct {
|
typedef struct {
|
||||||
int try_catch; // position to jump to on throw
|
int try_catch; // position to jump to on throw
|
||||||
int try_finally; // position to jump to for return
|
int try_finally; // :finally or :endtry position to jump to
|
||||||
} try_T;
|
} try_T;
|
||||||
|
|
||||||
|
// arguments to ISN_TRYCONT
|
||||||
|
typedef struct {
|
||||||
|
int tct_levels; // number of nested try statements
|
||||||
|
int tct_where; // position to jump to, WHILE or FOR
|
||||||
|
} trycont_T;
|
||||||
|
|
||||||
// arguments to ISN_ECHO
|
// arguments to ISN_ECHO
|
||||||
typedef struct {
|
typedef struct {
|
||||||
int echo_with_white; // :echo instead of :echon
|
int echo_with_white; // :echo instead of :echon
|
||||||
@@ -333,6 +340,7 @@ struct isn_S {
|
|||||||
jump_T jump;
|
jump_T jump;
|
||||||
forloop_T forloop;
|
forloop_T forloop;
|
||||||
try_T try;
|
try_T try;
|
||||||
|
trycont_T trycont;
|
||||||
cbfunc_T bfunc;
|
cbfunc_T bfunc;
|
||||||
cdfunc_T dfunc;
|
cdfunc_T dfunc;
|
||||||
cpfunc_T pfunc;
|
cpfunc_T pfunc;
|
||||||
|
@@ -1592,6 +1592,23 @@ generate_FOR(cctx_T *cctx, int loop_idx)
|
|||||||
|
|
||||||
return OK;
|
return OK;
|
||||||
}
|
}
|
||||||
|
/*
|
||||||
|
* Generate an ISN_TRYCONT instruction.
|
||||||
|
*/
|
||||||
|
static int
|
||||||
|
generate_TRYCONT(cctx_T *cctx, int levels, int where)
|
||||||
|
{
|
||||||
|
isn_T *isn;
|
||||||
|
|
||||||
|
RETURN_OK_IF_SKIP(cctx);
|
||||||
|
if ((isn = generate_instr(cctx, ISN_TRYCONT)) == NULL)
|
||||||
|
return FAIL;
|
||||||
|
isn->isn_arg.trycont.tct_levels = levels;
|
||||||
|
isn->isn_arg.trycont.tct_where = where;
|
||||||
|
|
||||||
|
return OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Generate an ISN_BCALL instruction.
|
* Generate an ISN_BCALL instruction.
|
||||||
@@ -7314,6 +7331,8 @@ compile_endwhile(char_u *arg, cctx_T *cctx)
|
|||||||
compile_continue(char_u *arg, cctx_T *cctx)
|
compile_continue(char_u *arg, cctx_T *cctx)
|
||||||
{
|
{
|
||||||
scope_T *scope = cctx->ctx_scope;
|
scope_T *scope = cctx->ctx_scope;
|
||||||
|
int try_scopes = 0;
|
||||||
|
int loop_label;
|
||||||
|
|
||||||
for (;;)
|
for (;;)
|
||||||
{
|
{
|
||||||
@@ -7322,15 +7341,29 @@ compile_continue(char_u *arg, cctx_T *cctx)
|
|||||||
emsg(_(e_continue));
|
emsg(_(e_continue));
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
if (scope->se_type == FOR_SCOPE || scope->se_type == WHILE_SCOPE)
|
if (scope->se_type == FOR_SCOPE)
|
||||||
|
{
|
||||||
|
loop_label = scope->se_u.se_for.fs_top_label;
|
||||||
break;
|
break;
|
||||||
|
}
|
||||||
|
if (scope->se_type == WHILE_SCOPE)
|
||||||
|
{
|
||||||
|
loop_label = scope->se_u.se_while.ws_top_label;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (scope->se_type == TRY_SCOPE)
|
||||||
|
++try_scopes;
|
||||||
scope = scope->se_outer;
|
scope = scope->se_outer;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Jump back to the FOR or WHILE instruction.
|
if (try_scopes > 0)
|
||||||
generate_JUMP(cctx, JUMP_ALWAYS,
|
// Inside one or more try/catch blocks we first need to jump to the
|
||||||
scope->se_type == FOR_SCOPE ? scope->se_u.se_for.fs_top_label
|
// "finally" or "endtry" to cleanup.
|
||||||
: scope->se_u.se_while.ws_top_label);
|
generate_TRYCONT(cctx, try_scopes, loop_label);
|
||||||
|
else
|
||||||
|
// Jump back to the FOR or WHILE instruction.
|
||||||
|
generate_JUMP(cctx, JUMP_ALWAYS, loop_label);
|
||||||
|
|
||||||
return arg;
|
return arg;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -7625,7 +7658,7 @@ compile_endtry(char_u *arg, cctx_T *cctx)
|
|||||||
{
|
{
|
||||||
scope_T *scope = cctx->ctx_scope;
|
scope_T *scope = cctx->ctx_scope;
|
||||||
garray_T *instr = &cctx->ctx_instr;
|
garray_T *instr = &cctx->ctx_instr;
|
||||||
isn_T *isn;
|
isn_T *try_isn;
|
||||||
|
|
||||||
// end block scope from :catch or :finally
|
// end block scope from :catch or :finally
|
||||||
if (scope != NULL && scope->se_type == BLOCK_SCOPE)
|
if (scope != NULL && scope->se_type == BLOCK_SCOPE)
|
||||||
@@ -7646,11 +7679,11 @@ compile_endtry(char_u *arg, cctx_T *cctx)
|
|||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
try_isn = ((isn_T *)instr->ga_data) + scope->se_u.se_try.ts_try_label;
|
||||||
if (cctx->ctx_skip != SKIP_YES)
|
if (cctx->ctx_skip != SKIP_YES)
|
||||||
{
|
{
|
||||||
isn = ((isn_T *)instr->ga_data) + scope->se_u.se_try.ts_try_label;
|
if (try_isn->isn_arg.try.try_catch == 0
|
||||||
if (isn->isn_arg.try.try_catch == 0
|
&& try_isn->isn_arg.try.try_finally == 0)
|
||||||
&& isn->isn_arg.try.try_finally == 0)
|
|
||||||
{
|
{
|
||||||
emsg(_(e_missing_catch_or_finally));
|
emsg(_(e_missing_catch_or_finally));
|
||||||
return NULL;
|
return NULL;
|
||||||
@@ -7670,21 +7703,27 @@ compile_endtry(char_u *arg, cctx_T *cctx)
|
|||||||
instr->ga_len, cctx);
|
instr->ga_len, cctx);
|
||||||
|
|
||||||
// End :catch or :finally scope: set value in ISN_TRY instruction
|
// End :catch or :finally scope: set value in ISN_TRY instruction
|
||||||
if (isn->isn_arg.try.try_catch == 0)
|
if (try_isn->isn_arg.try.try_catch == 0)
|
||||||
isn->isn_arg.try.try_catch = instr->ga_len;
|
try_isn->isn_arg.try.try_catch = instr->ga_len;
|
||||||
if (isn->isn_arg.try.try_finally == 0)
|
if (try_isn->isn_arg.try.try_finally == 0)
|
||||||
isn->isn_arg.try.try_finally = instr->ga_len;
|
try_isn->isn_arg.try.try_finally = instr->ga_len;
|
||||||
|
|
||||||
if (scope->se_u.se_try.ts_catch_label != 0)
|
if (scope->se_u.se_try.ts_catch_label != 0)
|
||||||
{
|
{
|
||||||
// Last catch without match jumps here
|
// Last catch without match jumps here
|
||||||
isn = ((isn_T *)instr->ga_data) + scope->se_u.se_try.ts_catch_label;
|
isn_T *isn = ((isn_T *)instr->ga_data)
|
||||||
|
+ scope->se_u.se_try.ts_catch_label;
|
||||||
isn->isn_arg.jump.jump_where = instr->ga_len;
|
isn->isn_arg.jump.jump_where = instr->ga_len;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
compile_endblock(cctx);
|
compile_endblock(cctx);
|
||||||
|
|
||||||
|
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 = cctx->ctx_instr.ga_len;
|
||||||
|
|
||||||
if (cctx->ctx_skip != SKIP_YES && generate_instr(cctx, ISN_ENDTRY) == NULL)
|
if (cctx->ctx_skip != SKIP_YES && generate_instr(cctx, ISN_ENDTRY) == NULL)
|
||||||
return NULL;
|
return NULL;
|
||||||
#ifdef FEAT_PROFILE
|
#ifdef FEAT_PROFILE
|
||||||
@@ -8850,6 +8889,7 @@ delete_instr(isn_T *isn)
|
|||||||
case ISN_STRSLICE:
|
case ISN_STRSLICE:
|
||||||
case ISN_THROW:
|
case ISN_THROW:
|
||||||
case ISN_TRY:
|
case ISN_TRY:
|
||||||
|
case ISN_TRYCONT:
|
||||||
case ISN_UNLETINDEX:
|
case ISN_UNLETINDEX:
|
||||||
case ISN_UNPACK:
|
case ISN_UNPACK:
|
||||||
// nothing allocated
|
// nothing allocated
|
||||||
|
@@ -27,8 +27,9 @@ typedef struct {
|
|||||||
int tcd_frame_idx; // ec_frame_idx at ISN_TRY
|
int tcd_frame_idx; // ec_frame_idx at ISN_TRY
|
||||||
int tcd_stack_len; // size of ectx.ec_stack 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_catch_idx; // instruction of the first catch
|
||||||
int tcd_finally_idx; // instruction of the finally block
|
int tcd_finally_idx; // instruction of the finally block or :endtry
|
||||||
int tcd_caught; // catch block entered
|
int tcd_caught; // catch block entered
|
||||||
|
int tcd_cont; // :continue encountered, jump here
|
||||||
int tcd_return; // when TRUE return from end of :finally
|
int tcd_return; // when TRUE return from end of :finally
|
||||||
} trycmd_T;
|
} trycmd_T;
|
||||||
|
|
||||||
@@ -2417,7 +2418,8 @@ call_def_function(
|
|||||||
+ trystack->ga_len - 1;
|
+ trystack->ga_len - 1;
|
||||||
if (trycmd != NULL
|
if (trycmd != NULL
|
||||||
&& trycmd->tcd_frame_idx == ectx.ec_frame_idx
|
&& trycmd->tcd_frame_idx == ectx.ec_frame_idx
|
||||||
&& trycmd->tcd_finally_idx != 0)
|
&& ectx.ec_instr[trycmd->tcd_finally_idx]
|
||||||
|
.isn_type != ISN_ENDTRY)
|
||||||
{
|
{
|
||||||
// jump to ":finally"
|
// jump to ":finally"
|
||||||
ectx.ec_iidx = trycmd->tcd_finally_idx;
|
ectx.ec_iidx = trycmd->tcd_finally_idx;
|
||||||
@@ -2610,6 +2612,34 @@ call_def_function(
|
|||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
case ISN_TRYCONT:
|
||||||
|
{
|
||||||
|
garray_T *trystack = &ectx.ec_trystack;
|
||||||
|
trycont_T *trycont = &iptr->isn_arg.trycont;
|
||||||
|
int i;
|
||||||
|
trycmd_T *trycmd;
|
||||||
|
int iidx = trycont->tct_where;
|
||||||
|
|
||||||
|
if (trystack->ga_len < trycont->tct_levels)
|
||||||
|
{
|
||||||
|
siemsg("TRYCONT: expected %d levels, found %d",
|
||||||
|
trycont->tct_levels, trystack->ga_len);
|
||||||
|
goto failed;
|
||||||
|
}
|
||||||
|
// Make :endtry jump to any outer try block and the last
|
||||||
|
// :endtry inside the loop to the loop start.
|
||||||
|
for (i = trycont->tct_levels; i > 0; --i)
|
||||||
|
{
|
||||||
|
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
|
// end of ":try" block
|
||||||
case ISN_ENDTRY:
|
case ISN_ENDTRY:
|
||||||
{
|
{
|
||||||
@@ -2640,6 +2670,10 @@ call_def_function(
|
|||||||
--ectx.ec_stack.ga_len;
|
--ectx.ec_stack.ga_len;
|
||||||
clear_tv(STACK_TV_BOT(0));
|
clear_tv(STACK_TV_BOT(0));
|
||||||
}
|
}
|
||||||
|
if (trycmd->tcd_cont)
|
||||||
|
// handling :continue: jump to outer try block or
|
||||||
|
// start of the loop
|
||||||
|
ectx.ec_iidx = trycmd->tcd_cont;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
@@ -4213,14 +4247,27 @@ ex_disassemble(exarg_T *eap)
|
|||||||
{
|
{
|
||||||
try_T *try = &iptr->isn_arg.try;
|
try_T *try = &iptr->isn_arg.try;
|
||||||
|
|
||||||
smsg("%4d TRY catch -> %d, finally -> %d", current,
|
smsg("%4d TRY catch -> %d, %s -> %d", current,
|
||||||
try->try_catch, try->try_finally);
|
try->try_catch,
|
||||||
|
instr[try->try_finally].isn_type == ISN_ENDTRY
|
||||||
|
? "end" : "finally",
|
||||||
|
try->try_finally);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case ISN_CATCH:
|
case ISN_CATCH:
|
||||||
// TODO
|
// TODO
|
||||||
smsg("%4d CATCH", current);
|
smsg("%4d CATCH", current);
|
||||||
break;
|
break;
|
||||||
|
case ISN_TRYCONT:
|
||||||
|
{
|
||||||
|
trycont_T *trycont = &iptr->isn_arg.trycont;
|
||||||
|
|
||||||
|
smsg("%4d TRY-CONTINUE %d level%s -> %d", current,
|
||||||
|
trycont->tct_levels,
|
||||||
|
trycont->tct_levels == 1 ? "" : "s",
|
||||||
|
trycont->tct_where);
|
||||||
|
}
|
||||||
|
break;
|
||||||
case ISN_ENDTRY:
|
case ISN_ENDTRY:
|
||||||
smsg("%4d ENDTRY", current);
|
smsg("%4d ENDTRY", current);
|
||||||
break;
|
break;
|
||||||
|
Reference in New Issue
Block a user