Patch 8.2.2651

5 views
Skip to first unread message

Bram Moolenaar

unread,
Mar 25, 2021, 4:13:24 PM3/25/21
to vim...@googlegroups.com

Patch 8.2.2651
Problem: Vim9: restoring command modifiers happens after jump.
Solution: Move the restore instruction to before the jump. (closes #8006)
Also handle for and while.
Files: src/vim9compile.c, src/vim9execute.c,
src/testdir/test_vim9_disassemble.vim


*** ../vim-8.2.2650/src/vim9compile.c 2021-03-21 22:12:31.448826619 +0100
--- src/vim9compile.c 2021-03-25 20:58:01.536755646 +0100
***************
*** 2172,2177 ****
--- 2172,2216 ----
return OK;
}

+ /*
+ * If an ISN_CMDMOD was just generated drop it.
+ */
+ static void
+ drop_cmdmod(cctx_T *cctx)
+ {
+ garray_T *instr = &cctx->ctx_instr;
+
+ // Drop any CMDMOD instruction
+ if (cctx->ctx_has_cmdmod
+ && ((isn_T *)instr->ga_data)[instr->ga_len - 1].isn_type
+ == ISN_CMDMOD)
+ {
+ --instr->ga_len;
+ cctx->ctx_has_cmdmod = FALSE;
+ }
+ }
+
+ /*
+ * Get the index of the current instruction.
+ * This compenstates for a preceding ISN_CMDMOD and ISN_PROF_START.
+ */
+ static int
+ current_instr_idx(cctx_T *cctx)
+ {
+ garray_T *instr = &cctx->ctx_instr;
+ int idx = instr->ga_len;
+
+ if (cctx->ctx_has_cmdmod && ((isn_T *)instr->ga_data)[idx - 1]
+ .isn_type == ISN_CMDMOD)
+ --idx;
+ #ifdef FEAT_PROFILE
+ if (cctx->ctx_profiling && ((isn_T *)instr->ga_data)[idx - 1]
+ .isn_type == ISN_PROF_START)
+ --idx;
+ #endif
+ return idx;
+ }
+
#ifdef FEAT_PROFILE
static void
may_generate_prof_end(cctx_T *cctx, int prof_lnum)
***************
*** 6877,6882 ****
--- 6916,6924 ----
return NULL;
}

+ // CMDMOD_REV must come before the jump
+ generate_undo_cmdmods(cctx);
+
scope = new_scope(cctx, IF_SCOPE);
if (scope == NULL)
return NULL;
***************
*** 6937,6960 ****
if (scope->se_u.se_if.is_seen_skip_not)
{
// A previous block was executed, skip over expression and bail out.
! // Do not count the "elseif" for profiling.
! #ifdef FEAT_PROFILE
! if (cctx->ctx_profiling && ((isn_T *)instr->ga_data)[instr->ga_len - 1]
! .isn_type == ISN_PROF_START)
! --instr->ga_len;
! #endif
skip_expr_cctx(&p, cctx);
return p;
}

if (cctx->ctx_skip == SKIP_UNKNOWN)
{
if (compile_jump_to_end(&scope->se_u.se_if.is_end_label,
JUMP_ALWAYS, cctx) == FAIL)
return NULL;
// previous "if" or "elseif" jumps here
isn = ((isn_T *)instr->ga_data) + scope->se_u.se_if.is_if_label;
isn->isn_arg.jump.jump_where = instr->ga_len;
}

// compile "expr"; if we know it evaluates to FALSE skip the block
--- 6979,7014 ----
if (scope->se_u.se_if.is_seen_skip_not)
{
// A previous block was executed, skip over expression and bail out.
! // Do not count the "elseif" for profiling and cmdmod
! instr->ga_len = current_instr_idx(cctx);
!
skip_expr_cctx(&p, cctx);
return p;
}

if (cctx->ctx_skip == SKIP_UNKNOWN)
{
+ int moved_cmdmod = FALSE;
+
+ // Move any CMDMOD instruction to after the jump
+ if (((isn_T *)instr->ga_data)[instr->ga_len - 1].isn_type == ISN_CMDMOD)
+ {
+ if (ga_grow(instr, 1) == FAIL)
+ return NULL;
+ ((isn_T *)instr->ga_data)[instr->ga_len] =
+ ((isn_T *)instr->ga_data)[instr->ga_len - 1];
+ --instr->ga_len;
+ moved_cmdmod = TRUE;
+ }
+
if (compile_jump_to_end(&scope->se_u.se_if.is_end_label,
JUMP_ALWAYS, cctx) == FAIL)
return NULL;
// previous "if" or "elseif" jumps here
isn = ((isn_T *)instr->ga_data) + scope->se_u.se_if.is_if_label;
isn->isn_arg.jump.jump_where = instr->ga_len;
+ if (moved_cmdmod)
+ ++instr->ga_len;
}

// compile "expr"; if we know it evaluates to FALSE skip the block
***************
*** 7007,7012 ****
--- 7061,7069 ----
if (bool_on_stack(cctx) == FAIL)
return NULL;

+ // CMDMOD_REV must come before the jump
+ generate_undo_cmdmods(cctx);
+
// "where" is set when ":elseif", "else" or ":endif" is found
scope->se_u.se_if.is_if_label = instr->ga_len;
generate_JUMP(cctx, JUMP_IF_FALSE, 0);
***************
*** 7090,7095 ****
--- 7147,7153 ----
garray_T *instr = &cctx->ctx_instr;
isn_T *isn;

+ drop_cmdmod(cctx);
if (scope == NULL || scope->se_type != IF_SCOPE)
{
emsg(_(e_endif_without_if));
***************
*** 7160,7166 ****
int var_count = 0;
int semicolon = FALSE;
size_t varlen;
- garray_T *instr = &cctx->ctx_instr;
garray_T *stack = &cctx->ctx_type_stack;
scope_T *scope;
lvar_T *loop_lvar; // loop iteration variable
--- 7218,7223 ----
***************
*** 7230,7237 ****
item_type = vartype->tt_member->tt_member;
}

// "for_end" is set when ":endfor" is found
! scope->se_u.se_for.fs_top_label = instr->ga_len;
generate_FOR(cctx, loop_lvar->lv_idx);

arg = arg_start;
--- 7287,7297 ----
item_type = vartype->tt_member->tt_member;
}

+ // CMDMOD_REV must come before the FOR instruction
+ generate_undo_cmdmods(cctx);
+
// "for_end" is set when ":endfor" is found
! scope->se_u.se_for.fs_top_label = current_instr_idx(cctx);
generate_FOR(cctx, loop_lvar->lv_idx);

arg = arg_start;
***************
*** 7333,7338 ****
--- 7393,7400 ----
forscope_T *forscope;
isn_T *isn;

+ drop_cmdmod(cctx);
+
if (scope == NULL || scope->se_type != FOR_SCOPE)
{
emsg(_(e_for));
***************
*** 7376,7395 ****
compile_while(char_u *arg, cctx_T *cctx)
{
char_u *p = arg;
- garray_T *instr = &cctx->ctx_instr;
scope_T *scope;

scope = new_scope(cctx, WHILE_SCOPE);
if (scope == NULL)
return NULL;

! // "endwhile" jumps back here, one before when profiling
! scope->se_u.se_while.ws_top_label = instr->ga_len;
! #ifdef FEAT_PROFILE
! if (cctx->ctx_profiling && ((isn_T *)instr->ga_data)[instr->ga_len - 1]
! .isn_type == ISN_PROF_START)
! --scope->se_u.se_while.ws_top_label;
! #endif

// compile "expr"
if (compile_expr0(&p, cctx) == FAIL)
--- 7438,7451 ----
compile_while(char_u *arg, cctx_T *cctx)
{
char_u *p = arg;
scope_T *scope;

scope = new_scope(cctx, WHILE_SCOPE);
if (scope == NULL)
return NULL;

! // "endwhile" jumps back here, one before when profiling or using cmdmods
! scope->se_u.se_while.ws_top_label = current_instr_idx(cctx);

// compile "expr"
if (compile_expr0(&p, cctx) == FAIL)
***************
*** 7403,7408 ****
--- 7459,7467 ----
if (bool_on_stack(cctx) == FAIL)
return FAIL;

+ // CMDMOD_REV must come before the jump
+ generate_undo_cmdmods(cctx);
+
// "while_end" is set when ":endwhile" is found
if (compile_jump_to_end(&scope->se_u.se_while.ws_end_label,
JUMP_IF_FALSE, cctx) == FAIL)
***************
*** 7420,7425 ****
--- 7479,7485 ----
scope_T *scope = cctx->ctx_scope;
garray_T *instr = &cctx->ctx_instr;

+ drop_cmdmod(cctx);
if (scope == NULL || scope->se_type != WHILE_SCOPE)
{
emsg(_(e_while));
*** ../vim-8.2.2650/src/vim9execute.c 2021-03-24 22:00:52.042056113 +0100
--- src/vim9execute.c 2021-03-25 20:32:43.784445430 +0100
***************
*** 796,801 ****
--- 796,816 ----
}

/*
+ * If command modifiers were applied restore them.
+ */
+ static void
+ may_restore_cmdmod(funclocal_T *funclocal)
+ {
+ if (funclocal->floc_restore_cmdmod)
+ {
+ cmdmod.cmod_filter_regmatch.regprog = NULL;
+ undo_cmdmod(&cmdmod);
+ cmdmod = funclocal->floc_save_cmdmod;
+ funclocal->floc_restore_cmdmod = FALSE;
+ }
+ }
+
+ /*
* Return TRUE if an error was given or CTRL-C was pressed.
*/
static int
***************
*** 2719,2726 ****
--- 2734,2744 ----
goto failed;
++idxtv->vval.v_number;
if (list == NULL || idxtv->vval.v_number >= list->lv_len)
+ {
// past the end of the list, jump to "endfor"
ectx.ec_iidx = iptr->isn_arg.forloop.for_end;
+ may_restore_cmdmod(&funclocal);
+ }
else if (list->lv_first == &range_list_item)
{
// non-materialized range() list
***************
*** 2755,2763 ****
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;

--- 2773,2784 ----
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;

***************
*** 2782,2794 ****
{
garray_T *trystack = &ectx.ec_trystack;

! if (funclocal.floc_restore_cmdmod)
! {
! cmdmod.cmod_filter_regmatch.regprog = NULL;
! undo_cmdmod(&cmdmod);
! cmdmod = funclocal.floc_save_cmdmod;
! funclocal.floc_restore_cmdmod = FALSE;
! }
if (trystack->ga_len > 0)
{
trycmd_T *trycmd = ((trycmd_T *)trystack->ga_data)
--- 2803,2809 ----
{
garray_T *trystack = &ectx.ec_trystack;

! may_restore_cmdmod(&funclocal);
if (trystack->ga_len > 0)
{
trycmd_T *trycmd = ((trycmd_T *)trystack->ga_data)
*** ../vim-8.2.2650/src/testdir/test_vim9_disassemble.vim 2021-03-17 18:42:04.442869185 +0100
--- src/testdir/test_vim9_disassemble.vim 2021-03-25 20:57:15.376870119 +0100
***************
*** 1896,1902 ****
'\d PUSHS "error"\_s*' ..
'\d ECHOERR 1\_s*' ..
'\d CMDMOD_REV\_s*' ..
! '\d RETURN 0',
res)
enddef

--- 1896,1990 ----
'\d PUSHS "error"\_s*' ..
'\d ECHOERR 1\_s*' ..
'\d CMDMOD_REV\_s*' ..
! '\d\+ RETURN 0',
! res)
! enddef
!
! def s:SilentIf()
! silent if 4 == g:five
! silent elseif 4 == g:five
! silent endif
! enddef
!
! def Test_silent_if()
! var res = execute('disass s:SilentIf')
! assert_match('<SNR>\d*_SilentIf\_s*' ..
! 'silent if 4 == g:five\_s*' ..
! '\d\+ CMDMOD silent\_s*' ..
! '\d\+ PUSHNR 4\_s*' ..
! '\d\+ LOADG g:five\_s*' ..
! '\d\+ COMPAREANY ==\_s*' ..
! '\d\+ CMDMOD_REV\_s*' ..
! '\d\+ JUMP_IF_FALSE -> \d\+\_s*' ..
! 'silent elseif 4 == g:five\_s*' ..
! '\d\+ JUMP -> \d\+\_s*' ..
! '\d\+ CMDMOD silent\_s*' ..
! '\d\+ PUSHNR 4\_s*' ..
! '\d\+ LOADG g:five\_s*' ..
! '\d\+ COMPAREANY ==\_s*' ..
! '\d\+ CMDMOD_REV\_s*' ..
! '\d\+ JUMP_IF_FALSE -> \d\+\_s*' ..
! 'silent endif\_s*' ..
! '\d\+ RETURN 0',
! res)
! enddef
!
! def s:SilentFor()
! silent for i in [0]
! silent endfor
! enddef
!
! def Test_silent_for()
! var res = execute('disass s:SilentFor')
! assert_match('<SNR>\d*_SilentFor\_s*' ..
! 'silent for i in \[0\]\_s*' ..
! '\d CMDMOD silent\_s*' ..
! '\d STORE -1 in $0\_s*' ..
! '\d PUSHNR 0\_s*' ..
! '\d NEWLIST size 1\_s*' ..
! '\d CMDMOD_REV\_s*' ..
! '5 FOR $0 -> 8\_s*' ..
! '\d STORE $1\_s*' ..
! 'silent endfor\_s*' ..
! '\d JUMP -> 5\_s*' ..
! '8 DROP\_s*' ..
! '\d RETURN 0\_s*',
! res)
! enddef
!
! def s:SilentWhile()
! silent while g:not
! silent endwhile
! enddef
!
! def Test_silent_while()
! var res = execute('disass s:SilentWhile')
! assert_match('<SNR>\d*_SilentWhile\_s*' ..
! 'silent while g:not\_s*' ..
! '0 CMDMOD silent\_s*' ..
! '\d LOADG g:not\_s*' ..
! '\d COND2BOOL\_s*' ..
! '\d CMDMOD_REV\_s*' ..
! '\d JUMP_IF_FALSE -> 6\_s*' ..
!
! 'silent endwhile\_s*' ..
! '\d JUMP -> 0\_s*' ..
! '6 RETURN 0\_s*',
! res)
! enddef
!
! def s:SilentReturn(): string
! silent return "done"
! enddef
!
! def Test_silent_return()
! var res = execute('disass s:SilentReturn')
! assert_match('<SNR>\d*_SilentReturn\_s*' ..
! 'silent return "done"\_s*' ..
! '\d CMDMOD silent\_s*' ..
! '\d PUSHS "done"\_s*' ..
! '\d CMDMOD_REV\_s*' ..
! '\d RETURN',
res)
enddef

***************
*** 1924,1942 ****
res)
enddef

- def s:SilentReturn(): string
- silent return "done"
- enddef
-
- def Test_silent_return()
- var res = execute('disass s:SilentReturn')
- assert_match('<SNR>\d*_SilentReturn\_s*' ..
- 'silent return "done"\_s*' ..
- '\d CMDMOD silent\_s*' ..
- '\d PUSHS "done"\_s*' ..
- '\d CMDMOD_REV\_s*' ..
- '\d RETURN',
- res)
- enddef

" vim: ts=8 sw=2 sts=2 expandtab tw=80 fdm=marker
--- 2012,2016 ----
*** ../vim-8.2.2650/src/version.c 2021-03-24 22:00:52.046056095 +0100
--- src/version.c 2021-03-25 19:35:19.530039581 +0100
***************
*** 752,753 ****
--- 752,755 ----
{ /* Add new patch number below this line */
+ /**/
+ 2651,
/**/

--
How To Keep A Healthy Level Of Insanity:
4. Put your garbage can on your desk and label it "in".

/// Bram Moolenaar -- Br...@Moolenaar.net -- http://www.Moolenaar.net \\\
/// \\\
\\\ sponsor Vim, vote for features -- http://www.Vim.org/sponsor/ ///
\\\ help me help AIDS victims -- http://ICCF-Holland.org ///
Reply all
Reply to author
Forward
0 new messages