Mercurial > vim
diff src/vim9compile.c @ 24220:a7a9176bb542 v8.2.2651
patch 8.2.2651: Vim9: restoring command modifiers happens after jump
Commit: https://github.com/vim/vim/commit/a91a71322dc2e6a1640e73b6da1f1a2f94f39a54
Author: Bram Moolenaar <Bram@vim.org>
Date: Thu Mar 25 21:12:15 2021 +0100
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 https://github.com/vim/vim/issues/8006)
Also handle for and while.
author | Bram Moolenaar <Bram@vim.org> |
---|---|
date | Thu, 25 Mar 2021 21:15:04 +0100 |
parents | 5f3a2d31c48d |
children | a2e6029d354e |
line wrap: on
line diff
--- a/src/vim9compile.c +++ b/src/vim9compile.c @@ -2172,6 +2172,45 @@ generate_undo_cmdmods(cctx_T *cctx) 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,6 +6916,9 @@ compile_if(char_u *arg, cctx_T *cctx) 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,24 +6979,36 @@ compile_elseif(char_u *arg, cctx_T *cctx 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 + // 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,6 +7061,9 @@ compile_elseif(char_u *arg, cctx_T *cctx 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,6 +7147,7 @@ compile_endif(char_u *arg, cctx_T *cctx) 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,7 +7218,6 @@ compile_for(char_u *arg_start, cctx_T *c 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 @@ -7230,8 +7287,11 @@ compile_for(char_u *arg_start, cctx_T *c 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 = instr->ga_len; + scope->se_u.se_for.fs_top_label = current_instr_idx(cctx); generate_FOR(cctx, loop_lvar->lv_idx); arg = arg_start; @@ -7333,6 +7393,8 @@ compile_endfor(char_u *arg, cctx_T *cctx forscope_T *forscope; isn_T *isn; + drop_cmdmod(cctx); + if (scope == NULL || scope->se_type != FOR_SCOPE) { emsg(_(e_for)); @@ -7376,20 +7438,14 @@ compile_endfor(char_u *arg, cctx_T *cctx 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 + // "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,6 +7459,9 @@ compile_while(char_u *arg, cctx_T *cctx) 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,6 +7479,7 @@ compile_endwhile(char_u *arg, cctx_T *cc 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));