0
0
mirror of https://github.com/vim/vim.git synced 2025-09-23 03:43:49 -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:
Bram Moolenaar
2021-02-13 15:02:46 +01:00
parent 31842cd077
commit c150c09ec4
6 changed files with 190 additions and 19 deletions

View File

@@ -1592,6 +1592,23 @@ generate_FOR(cctx_T *cctx, int loop_idx)
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.
@@ -7314,6 +7331,8 @@ compile_endwhile(char_u *arg, cctx_T *cctx)
compile_continue(char_u *arg, cctx_T *cctx)
{
scope_T *scope = cctx->ctx_scope;
int try_scopes = 0;
int loop_label;
for (;;)
{
@@ -7322,15 +7341,29 @@ compile_continue(char_u *arg, cctx_T *cctx)
emsg(_(e_continue));
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;
}
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;
}
// Jump back to the FOR or WHILE instruction.
generate_JUMP(cctx, JUMP_ALWAYS,
scope->se_type == FOR_SCOPE ? scope->se_u.se_for.fs_top_label
: scope->se_u.se_while.ws_top_label);
if (try_scopes > 0)
// Inside one or more try/catch blocks we first need to jump to the
// "finally" or "endtry" to cleanup.
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;
}
@@ -7625,7 +7658,7 @@ compile_endtry(char_u *arg, cctx_T *cctx)
{
scope_T *scope = cctx->ctx_scope;
garray_T *instr = &cctx->ctx_instr;
isn_T *isn;
isn_T *try_isn;
// end block scope from :catch or :finally
if (scope != NULL && scope->se_type == BLOCK_SCOPE)
@@ -7646,11 +7679,11 @@ compile_endtry(char_u *arg, cctx_T *cctx)
return NULL;
}
try_isn = ((isn_T *)instr->ga_data) + scope->se_u.se_try.ts_try_label;
if (cctx->ctx_skip != SKIP_YES)
{
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_finally == 0)
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;
@@ -7670,21 +7703,27 @@ compile_endtry(char_u *arg, cctx_T *cctx)
instr->ga_len, cctx);
// End :catch or :finally scope: set value in ISN_TRY instruction
if (isn->isn_arg.try.try_catch == 0)
isn->isn_arg.try.try_catch = instr->ga_len;
if (isn->isn_arg.try.try_finally == 0)
isn->isn_arg.try.try_finally = instr->ga_len;
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
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;
}
}
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)
return NULL;
#ifdef FEAT_PROFILE
@@ -8850,6 +8889,7 @@ delete_instr(isn_T *isn)
case ISN_STRSLICE:
case ISN_THROW:
case ISN_TRY:
case ISN_TRYCONT:
case ISN_UNLETINDEX:
case ISN_UNPACK:
// nothing allocated