To: vim_dev@googlegroups.com Subject: Patch 8.2.2651 Fcc: outbox From: Bram Moolenaar Mime-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ------------ 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('\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('\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('\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('\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('\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 -- Bram@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 ///