Skip to content

Commit a91a713

Browse files
committed
patch 8.2.2651: Vim9: restoring command modifiers happens after jump
Problem: Vim9: restoring command modifiers happens after jump. Solution: Move the restore instruction to before the jump. (closes #8006) Also handle for and while.
1 parent 2fecb53 commit a91a713

4 files changed

Lines changed: 192 additions & 41 deletions

File tree

src/testdir/test_vim9_disassemble.vim

Lines changed: 89 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1896,7 +1896,95 @@ def Test_silent()
18961896
'\d PUSHS "error"\_s*' ..
18971897
'\d ECHOERR 1\_s*' ..
18981898
'\d CMDMOD_REV\_s*' ..
1899-
'\d RETURN 0',
1899+
'\d\+ RETURN 0',
1900+
res)
1901+
enddef
1902+
1903+
def s:SilentIf()
1904+
silent if 4 == g:five
1905+
silent elseif 4 == g:five
1906+
silent endif
1907+
enddef
1908+
1909+
def Test_silent_if()
1910+
var res = execute('disass s:SilentIf')
1911+
assert_match('<SNR>\d*_SilentIf\_s*' ..
1912+
'silent if 4 == g:five\_s*' ..
1913+
'\d\+ CMDMOD silent\_s*' ..
1914+
'\d\+ PUSHNR 4\_s*' ..
1915+
'\d\+ LOADG g:five\_s*' ..
1916+
'\d\+ COMPAREANY ==\_s*' ..
1917+
'\d\+ CMDMOD_REV\_s*' ..
1918+
'\d\+ JUMP_IF_FALSE -> \d\+\_s*' ..
1919+
'silent elseif 4 == g:five\_s*' ..
1920+
'\d\+ JUMP -> \d\+\_s*' ..
1921+
'\d\+ CMDMOD silent\_s*' ..
1922+
'\d\+ PUSHNR 4\_s*' ..
1923+
'\d\+ LOADG g:five\_s*' ..
1924+
'\d\+ COMPAREANY ==\_s*' ..
1925+
'\d\+ CMDMOD_REV\_s*' ..
1926+
'\d\+ JUMP_IF_FALSE -> \d\+\_s*' ..
1927+
'silent endif\_s*' ..
1928+
'\d\+ RETURN 0',
1929+
res)
1930+
enddef
1931+
1932+
def s:SilentFor()
1933+
silent for i in [0]
1934+
silent endfor
1935+
enddef
1936+
1937+
def Test_silent_for()
1938+
var res = execute('disass s:SilentFor')
1939+
assert_match('<SNR>\d*_SilentFor\_s*' ..
1940+
'silent for i in \[0\]\_s*' ..
1941+
'\d CMDMOD silent\_s*' ..
1942+
'\d STORE -1 in $0\_s*' ..
1943+
'\d PUSHNR 0\_s*' ..
1944+
'\d NEWLIST size 1\_s*' ..
1945+
'\d CMDMOD_REV\_s*' ..
1946+
'5 FOR $0 -> 8\_s*' ..
1947+
'\d STORE $1\_s*' ..
1948+
'silent endfor\_s*' ..
1949+
'\d JUMP -> 5\_s*' ..
1950+
'8 DROP\_s*' ..
1951+
'\d RETURN 0\_s*',
1952+
res)
1953+
enddef
1954+
1955+
def s:SilentWhile()
1956+
silent while g:not
1957+
silent endwhile
1958+
enddef
1959+
1960+
def Test_silent_while()
1961+
var res = execute('disass s:SilentWhile')
1962+
assert_match('<SNR>\d*_SilentWhile\_s*' ..
1963+
'silent while g:not\_s*' ..
1964+
'0 CMDMOD silent\_s*' ..
1965+
'\d LOADG g:not\_s*' ..
1966+
'\d COND2BOOL\_s*' ..
1967+
'\d CMDMOD_REV\_s*' ..
1968+
'\d JUMP_IF_FALSE -> 6\_s*' ..
1969+
1970+
'silent endwhile\_s*' ..
1971+
'\d JUMP -> 0\_s*' ..
1972+
'6 RETURN 0\_s*',
1973+
res)
1974+
enddef
1975+
1976+
def s:SilentReturn(): string
1977+
silent return "done"
1978+
enddef
1979+
1980+
def Test_silent_return()
1981+
var res = execute('disass s:SilentReturn')
1982+
assert_match('<SNR>\d*_SilentReturn\_s*' ..
1983+
'silent return "done"\_s*' ..
1984+
'\d CMDMOD silent\_s*' ..
1985+
'\d PUSHS "done"\_s*' ..
1986+
'\d CMDMOD_REV\_s*' ..
1987+
'\d RETURN',
19001988
res)
19011989
enddef
19021990

@@ -1924,19 +2012,5 @@ def Test_profiled()
19242012
res)
19252013
enddef
19262014

1927-
def s:SilentReturn(): string
1928-
silent return "done"
1929-
enddef
1930-
1931-
def Test_silent_return()
1932-
var res = execute('disass s:SilentReturn')
1933-
assert_match('<SNR>\d*_SilentReturn\_s*' ..
1934-
'silent return "done"\_s*' ..
1935-
'\d CMDMOD silent\_s*' ..
1936-
'\d PUSHS "done"\_s*' ..
1937-
'\d CMDMOD_REV\_s*' ..
1938-
'\d RETURN',
1939-
res)
1940-
enddef
19412015

19422016
" vim: ts=8 sw=2 sts=2 expandtab tw=80 fdm=marker

src/version.c

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -750,6 +750,8 @@ static char *(features[]) =
750750

751751
static int included_patches[] =
752752
{ /* Add new patch number below this line */
753+
/**/
754+
2651,
753755
/**/
754756
2650,
755757
/**/

src/vim9compile.c

Lines changed: 76 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -2172,6 +2172,45 @@ generate_undo_cmdmods(cctx_T *cctx)
21722172
return OK;
21732173
}
21742174

2175+
/*
2176+
* If an ISN_CMDMOD was just generated drop it.
2177+
*/
2178+
static void
2179+
drop_cmdmod(cctx_T *cctx)
2180+
{
2181+
garray_T *instr = &cctx->ctx_instr;
2182+
2183+
// Drop any CMDMOD instruction
2184+
if (cctx->ctx_has_cmdmod
2185+
&& ((isn_T *)instr->ga_data)[instr->ga_len - 1].isn_type
2186+
== ISN_CMDMOD)
2187+
{
2188+
--instr->ga_len;
2189+
cctx->ctx_has_cmdmod = FALSE;
2190+
}
2191+
}
2192+
2193+
/*
2194+
* Get the index of the current instruction.
2195+
* This compenstates for a preceding ISN_CMDMOD and ISN_PROF_START.
2196+
*/
2197+
static int
2198+
current_instr_idx(cctx_T *cctx)
2199+
{
2200+
garray_T *instr = &cctx->ctx_instr;
2201+
int idx = instr->ga_len;
2202+
2203+
if (cctx->ctx_has_cmdmod && ((isn_T *)instr->ga_data)[idx - 1]
2204+
.isn_type == ISN_CMDMOD)
2205+
--idx;
2206+
#ifdef FEAT_PROFILE
2207+
if (cctx->ctx_profiling && ((isn_T *)instr->ga_data)[idx - 1]
2208+
.isn_type == ISN_PROF_START)
2209+
--idx;
2210+
#endif
2211+
return idx;
2212+
}
2213+
21752214
#ifdef FEAT_PROFILE
21762215
static void
21772216
may_generate_prof_end(cctx_T *cctx, int prof_lnum)
@@ -6877,6 +6916,9 @@ compile_if(char_u *arg, cctx_T *cctx)
68776916
return NULL;
68786917
}
68796918

6919+
// CMDMOD_REV must come before the jump
6920+
generate_undo_cmdmods(cctx);
6921+
68806922
scope = new_scope(cctx, IF_SCOPE);
68816923
if (scope == NULL)
68826924
return NULL;
@@ -6937,24 +6979,36 @@ compile_elseif(char_u *arg, cctx_T *cctx)
69376979
if (scope->se_u.se_if.is_seen_skip_not)
69386980
{
69396981
// A previous block was executed, skip over expression and bail out.
6940-
// Do not count the "elseif" for profiling.
6941-
#ifdef FEAT_PROFILE
6942-
if (cctx->ctx_profiling && ((isn_T *)instr->ga_data)[instr->ga_len - 1]
6943-
.isn_type == ISN_PROF_START)
6944-
--instr->ga_len;
6945-
#endif
6982+
// Do not count the "elseif" for profiling and cmdmod
6983+
instr->ga_len = current_instr_idx(cctx);
6984+
69466985
skip_expr_cctx(&p, cctx);
69476986
return p;
69486987
}
69496988

69506989
if (cctx->ctx_skip == SKIP_UNKNOWN)
69516990
{
6991+
int moved_cmdmod = FALSE;
6992+
6993+
// Move any CMDMOD instruction to after the jump
6994+
if (((isn_T *)instr->ga_data)[instr->ga_len - 1].isn_type == ISN_CMDMOD)
6995+
{
6996+
if (ga_grow(instr, 1) == FAIL)
6997+
return NULL;
6998+
((isn_T *)instr->ga_data)[instr->ga_len] =
6999+
((isn_T *)instr->ga_data)[instr->ga_len - 1];
7000+
--instr->ga_len;
7001+
moved_cmdmod = TRUE;
7002+
}
7003+
69527004
if (compile_jump_to_end(&scope->se_u.se_if.is_end_label,
69537005
JUMP_ALWAYS, cctx) == FAIL)
69547006
return NULL;
69557007
// previous "if" or "elseif" jumps here
69567008
isn = ((isn_T *)instr->ga_data) + scope->se_u.se_if.is_if_label;
69577009
isn->isn_arg.jump.jump_where = instr->ga_len;
7010+
if (moved_cmdmod)
7011+
++instr->ga_len;
69587012
}
69597013

69607014
// compile "expr"; if we know it evaluates to FALSE skip the block
@@ -7007,6 +7061,9 @@ compile_elseif(char_u *arg, cctx_T *cctx)
70077061
if (bool_on_stack(cctx) == FAIL)
70087062
return NULL;
70097063

7064+
// CMDMOD_REV must come before the jump
7065+
generate_undo_cmdmods(cctx);
7066+
70107067
// "where" is set when ":elseif", "else" or ":endif" is found
70117068
scope->se_u.se_if.is_if_label = instr->ga_len;
70127069
generate_JUMP(cctx, JUMP_IF_FALSE, 0);
@@ -7090,6 +7147,7 @@ compile_endif(char_u *arg, cctx_T *cctx)
70907147
garray_T *instr = &cctx->ctx_instr;
70917148
isn_T *isn;
70927149

7150+
drop_cmdmod(cctx);
70937151
if (scope == NULL || scope->se_type != IF_SCOPE)
70947152
{
70957153
emsg(_(e_endif_without_if));
@@ -7160,7 +7218,6 @@ compile_for(char_u *arg_start, cctx_T *cctx)
71607218
int var_count = 0;
71617219
int semicolon = FALSE;
71627220
size_t varlen;
7163-
garray_T *instr = &cctx->ctx_instr;
71647221
garray_T *stack = &cctx->ctx_type_stack;
71657222
scope_T *scope;
71667223
lvar_T *loop_lvar; // loop iteration variable
@@ -7230,8 +7287,11 @@ compile_for(char_u *arg_start, cctx_T *cctx)
72307287
item_type = vartype->tt_member->tt_member;
72317288
}
72327289

7290+
// CMDMOD_REV must come before the FOR instruction
7291+
generate_undo_cmdmods(cctx);
7292+
72337293
// "for_end" is set when ":endfor" is found
7234-
scope->se_u.se_for.fs_top_label = instr->ga_len;
7294+
scope->se_u.se_for.fs_top_label = current_instr_idx(cctx);
72357295
generate_FOR(cctx, loop_lvar->lv_idx);
72367296

72377297
arg = arg_start;
@@ -7333,6 +7393,8 @@ compile_endfor(char_u *arg, cctx_T *cctx)
73337393
forscope_T *forscope;
73347394
isn_T *isn;
73357395

7396+
drop_cmdmod(cctx);
7397+
73367398
if (scope == NULL || scope->se_type != FOR_SCOPE)
73377399
{
73387400
emsg(_(e_for));
@@ -7376,20 +7438,14 @@ compile_endfor(char_u *arg, cctx_T *cctx)
73767438
compile_while(char_u *arg, cctx_T *cctx)
73777439
{
73787440
char_u *p = arg;
7379-
garray_T *instr = &cctx->ctx_instr;
73807441
scope_T *scope;
73817442

73827443
scope = new_scope(cctx, WHILE_SCOPE);
73837444
if (scope == NULL)
73847445
return NULL;
73857446

7386-
// "endwhile" jumps back here, one before when profiling
7387-
scope->se_u.se_while.ws_top_label = instr->ga_len;
7388-
#ifdef FEAT_PROFILE
7389-
if (cctx->ctx_profiling && ((isn_T *)instr->ga_data)[instr->ga_len - 1]
7390-
.isn_type == ISN_PROF_START)
7391-
--scope->se_u.se_while.ws_top_label;
7392-
#endif
7447+
// "endwhile" jumps back here, one before when profiling or using cmdmods
7448+
scope->se_u.se_while.ws_top_label = current_instr_idx(cctx);
73937449

73947450
// compile "expr"
73957451
if (compile_expr0(&p, cctx) == FAIL)
@@ -7403,6 +7459,9 @@ compile_while(char_u *arg, cctx_T *cctx)
74037459
if (bool_on_stack(cctx) == FAIL)
74047460
return FAIL;
74057461

7462+
// CMDMOD_REV must come before the jump
7463+
generate_undo_cmdmods(cctx);
7464+
74067465
// "while_end" is set when ":endwhile" is found
74077466
if (compile_jump_to_end(&scope->se_u.se_while.ws_end_label,
74087467
JUMP_IF_FALSE, cctx) == FAIL)
@@ -7420,6 +7479,7 @@ compile_endwhile(char_u *arg, cctx_T *cctx)
74207479
scope_T *scope = cctx->ctx_scope;
74217480
garray_T *instr = &cctx->ctx_instr;
74227481

7482+
drop_cmdmod(cctx);
74237483
if (scope == NULL || scope->se_type != WHILE_SCOPE)
74247484
{
74257485
emsg(_(e_while));

src/vim9execute.c

Lines changed: 25 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -795,6 +795,21 @@ call_ufunc(
795795
return OK;
796796
}
797797

798+
/*
799+
* If command modifiers were applied restore them.
800+
*/
801+
static void
802+
may_restore_cmdmod(funclocal_T *funclocal)
803+
{
804+
if (funclocal->floc_restore_cmdmod)
805+
{
806+
cmdmod.cmod_filter_regmatch.regprog = NULL;
807+
undo_cmdmod(&cmdmod);
808+
cmdmod = funclocal->floc_save_cmdmod;
809+
funclocal->floc_restore_cmdmod = FALSE;
810+
}
811+
}
812+
798813
/*
799814
* Return TRUE if an error was given or CTRL-C was pressed.
800815
*/
@@ -2719,8 +2734,11 @@ call_def_function(
27192734
goto failed;
27202735
++idxtv->vval.v_number;
27212736
if (list == NULL || idxtv->vval.v_number >= list->lv_len)
2737+
{
27222738
// past the end of the list, jump to "endfor"
27232739
ectx.ec_iidx = iptr->isn_arg.forloop.for_end;
2740+
may_restore_cmdmod(&funclocal);
2741+
}
27242742
else if (list->lv_first == &range_list_item)
27252743
{
27262744
// non-materialized range() list
@@ -2755,9 +2773,12 @@ call_def_function(
27552773
CLEAR_POINTER(trycmd);
27562774
trycmd->tcd_frame_idx = ectx.ec_frame_idx;
27572775
trycmd->tcd_stack_len = ectx.ec_stack.ga_len;
2758-
trycmd->tcd_catch_idx = iptr->isn_arg.try.try_ref->try_catch;
2759-
trycmd->tcd_finally_idx = iptr->isn_arg.try.try_ref->try_finally;
2760-
trycmd->tcd_endtry_idx = iptr->isn_arg.try.try_ref->try_endtry;
2776+
trycmd->tcd_catch_idx =
2777+
iptr->isn_arg.try.try_ref->try_catch;
2778+
trycmd->tcd_finally_idx =
2779+
iptr->isn_arg.try.try_ref->try_finally;
2780+
trycmd->tcd_endtry_idx =
2781+
iptr->isn_arg.try.try_ref->try_endtry;
27612782
}
27622783
break;
27632784

@@ -2782,13 +2803,7 @@ call_def_function(
27822803
{
27832804
garray_T *trystack = &ectx.ec_trystack;
27842805

2785-
if (funclocal.floc_restore_cmdmod)
2786-
{
2787-
cmdmod.cmod_filter_regmatch.regprog = NULL;
2788-
undo_cmdmod(&cmdmod);
2789-
cmdmod = funclocal.floc_save_cmdmod;
2790-
funclocal.floc_restore_cmdmod = FALSE;
2791-
}
2806+
may_restore_cmdmod(&funclocal);
27922807
if (trystack->ga_len > 0)
27932808
{
27942809
trycmd_T *trycmd = ((trycmd_T *)trystack->ga_data)

0 commit comments

Comments
 (0)