# HG changeset patch # User Bram Moolenaar # Date 1629486006 -7200 # Node ID 6ed39aa92cb917c65af07afd5b6b4ad938cb92ce # Parent cc4bdae845228bd526501765b22d66777c47725c patch 8.2.3361: Vim9: crash with nested :while Commit: https://github.com/vim/vim/commit/d895b1d918f7d56dd9dd564be4820082ba6492e9 Author: rbtnn Date: Fri Aug 20 20:54:25 2021 +0200 patch 8.2.3361: Vim9: crash with nested :while Problem: Vim9: crash with nested :while. Solution: Handle skipping better. (Naruhiko Nishino, closes https://github.com/vim/vim/issues/8778) diff --git a/src/testdir/test_vim9_script.vim b/src/testdir/test_vim9_script.vim --- a/src/testdir/test_vim9_script.vim +++ b/src/testdir/test_vim9_script.vim @@ -2856,6 +2856,89 @@ def Test_for_loop_with_try_continue() CheckDefAndScriptSuccess(lines) enddef +def Test_while_skipped_block() + # test skipped blocks at outside of function + var lines =<< trim END + var result = [] + var n = 0 + if true + n = 1 + while n < 3 + result += [n] + n += 1 + endwhile + else + n = 3 + while n < 5 + result += [n] + n += 1 + endwhile + endif + assert_equal([1, 2], result) + + result = [] + if false + n = 1 + while n < 3 + result += [n] + n += 1 + endwhile + else + n = 3 + while n < 5 + result += [n] + n += 1 + endwhile + endif + assert_equal([3, 4], result) + END + CheckDefAndScriptSuccess(lines) + + # test skipped blocks at inside of function + lines =<< trim END + def DefTrue() + var result = [] + var n = 0 + if true + n = 1 + while n < 3 + result += [n] + n += 1 + endwhile + else + n = 3 + while n < 5 + result += [n] + n += 1 + endwhile + endif + assert_equal([1, 2], result) + enddef + DefTrue() + + def DefFalse() + var result = [] + var n = 0 + if false + n = 1 + while n < 3 + result += [n] + n += 1 + endwhile + else + n = 3 + while n < 5 + result += [n] + n += 1 + endwhile + endif + assert_equal([3, 4], result) + enddef + DefFalse() + END + CheckDefAndScriptSuccess(lines) +enddef + def Test_while_loop() var result = '' var cnt = 0 diff --git a/src/version.c b/src/version.c --- a/src/version.c +++ b/src/version.c @@ -756,6 +756,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ /**/ + 3361, +/**/ 3360, /**/ 3359, diff --git a/src/vim9compile.c b/src/vim9compile.c --- a/src/vim9compile.c +++ b/src/vim9compile.c @@ -4469,8 +4469,6 @@ compile_subscript( // dict member: dict[key] // string index: text[123] // blob index: blob[123] - // TODO: more arguments - // TODO: recognize list or dict at runtime if (generate_ppconst(cctx, ppconst) == FAIL) return FAIL; ppconst->pp_is_const = FALSE; @@ -8267,22 +8265,26 @@ compile_while(char_u *arg, cctx_T *cctx) // compile "expr" if (compile_expr0(&p, cctx) == FAIL) return NULL; + if (!ends_excmd2(arg, skipwhite(p))) { semsg(_(e_trailing_arg), p); return NULL; } - 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, + if (cctx->ctx_skip != SKIP_YES) + { + 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) - return FAIL; + return FAIL; + } return p; } @@ -8304,20 +8306,23 @@ compile_endwhile(char_u *arg, cctx_T *cc return NULL; } cctx->ctx_scope = scope->se_outer; - unwind_locals(cctx, scope->se_local_count); + if (cctx->ctx_skip != SKIP_YES) + { + unwind_locals(cctx, scope->se_local_count); #ifdef FEAT_PROFILE - // count the endwhile before jumping - may_generate_prof_end(cctx, cctx->ctx_lnum); + // count the endwhile before jumping + may_generate_prof_end(cctx, cctx->ctx_lnum); #endif - // At end of ":for" scope jump back to the FOR instruction. - generate_JUMP(cctx, JUMP_ALWAYS, scope->se_u.se_while.ws_top_label); - - // Fill in the "end" label in the WHILE statement so it can jump here. - // And in any jumps for ":break" - compile_fill_jump_to_end(&scope->se_u.se_while.ws_end_label, + // At end of ":for" scope jump back to the FOR instruction. + generate_JUMP(cctx, JUMP_ALWAYS, scope->se_u.se_while.ws_top_label); + + // Fill in the "end" label in the WHILE statement so it can jump here. + // And in any jumps for ":break" + compile_fill_jump_to_end(&scope->se_u.se_while.ws_end_label, instr->ga_len, cctx); + } vim_free(scope);