Mercurial > vim
comparison 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 |
comparison
equal
deleted
inserted
replaced
24219:529521098193 | 24220:a7a9176bb542 |
---|---|
2168 { | 2168 { |
2169 if (cctx->ctx_has_cmdmod && generate_instr(cctx, ISN_CMDMOD_REV) == NULL) | 2169 if (cctx->ctx_has_cmdmod && generate_instr(cctx, ISN_CMDMOD_REV) == NULL) |
2170 return FAIL; | 2170 return FAIL; |
2171 cctx->ctx_has_cmdmod = FALSE; | 2171 cctx->ctx_has_cmdmod = FALSE; |
2172 return OK; | 2172 return OK; |
2173 } | |
2174 | |
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; | |
2173 } | 2212 } |
2174 | 2213 |
2175 #ifdef FEAT_PROFILE | 2214 #ifdef FEAT_PROFILE |
2176 static void | 2215 static void |
2177 may_generate_prof_end(cctx_T *cctx, int prof_lnum) | 2216 may_generate_prof_end(cctx_T *cctx, int prof_lnum) |
6875 return NULL; | 6914 return NULL; |
6876 if (bool_on_stack(cctx) == FAIL) | 6915 if (bool_on_stack(cctx) == FAIL) |
6877 return NULL; | 6916 return NULL; |
6878 } | 6917 } |
6879 | 6918 |
6919 // CMDMOD_REV must come before the jump | |
6920 generate_undo_cmdmods(cctx); | |
6921 | |
6880 scope = new_scope(cctx, IF_SCOPE); | 6922 scope = new_scope(cctx, IF_SCOPE); |
6881 if (scope == NULL) | 6923 if (scope == NULL) |
6882 return NULL; | 6924 return NULL; |
6883 scope->se_skip_save = skip_save; | 6925 scope->se_skip_save = skip_save; |
6884 // "is_had_return" will be reset if any block does not end in :return | 6926 // "is_had_return" will be reset if any block does not end in :return |
6935 scope->se_u.se_if.is_seen_skip_not = TRUE; | 6977 scope->se_u.se_if.is_seen_skip_not = TRUE; |
6936 } | 6978 } |
6937 if (scope->se_u.se_if.is_seen_skip_not) | 6979 if (scope->se_u.se_if.is_seen_skip_not) |
6938 { | 6980 { |
6939 // A previous block was executed, skip over expression and bail out. | 6981 // A previous block was executed, skip over expression and bail out. |
6940 // Do not count the "elseif" for profiling. | 6982 // Do not count the "elseif" for profiling and cmdmod |
6941 #ifdef FEAT_PROFILE | 6983 instr->ga_len = current_instr_idx(cctx); |
6942 if (cctx->ctx_profiling && ((isn_T *)instr->ga_data)[instr->ga_len - 1] | 6984 |
6943 .isn_type == ISN_PROF_START) | |
6944 --instr->ga_len; | |
6945 #endif | |
6946 skip_expr_cctx(&p, cctx); | 6985 skip_expr_cctx(&p, cctx); |
6947 return p; | 6986 return p; |
6948 } | 6987 } |
6949 | 6988 |
6950 if (cctx->ctx_skip == SKIP_UNKNOWN) | 6989 if (cctx->ctx_skip == SKIP_UNKNOWN) |
6951 { | 6990 { |
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 | |
6952 if (compile_jump_to_end(&scope->se_u.se_if.is_end_label, | 7004 if (compile_jump_to_end(&scope->se_u.se_if.is_end_label, |
6953 JUMP_ALWAYS, cctx) == FAIL) | 7005 JUMP_ALWAYS, cctx) == FAIL) |
6954 return NULL; | 7006 return NULL; |
6955 // previous "if" or "elseif" jumps here | 7007 // previous "if" or "elseif" jumps here |
6956 isn = ((isn_T *)instr->ga_data) + scope->se_u.se_if.is_if_label; | 7008 isn = ((isn_T *)instr->ga_data) + scope->se_u.se_if.is_if_label; |
6957 isn->isn_arg.jump.jump_where = instr->ga_len; | 7009 isn->isn_arg.jump.jump_where = instr->ga_len; |
7010 if (moved_cmdmod) | |
7011 ++instr->ga_len; | |
6958 } | 7012 } |
6959 | 7013 |
6960 // compile "expr"; if we know it evaluates to FALSE skip the block | 7014 // compile "expr"; if we know it evaluates to FALSE skip the block |
6961 CLEAR_FIELD(ppconst); | 7015 CLEAR_FIELD(ppconst); |
6962 if (cctx->ctx_skip == SKIP_YES) | 7016 if (cctx->ctx_skip == SKIP_YES) |
7005 if (generate_ppconst(cctx, &ppconst) == FAIL) | 7059 if (generate_ppconst(cctx, &ppconst) == FAIL) |
7006 return NULL; | 7060 return NULL; |
7007 if (bool_on_stack(cctx) == FAIL) | 7061 if (bool_on_stack(cctx) == FAIL) |
7008 return NULL; | 7062 return NULL; |
7009 | 7063 |
7064 // CMDMOD_REV must come before the jump | |
7065 generate_undo_cmdmods(cctx); | |
7066 | |
7010 // "where" is set when ":elseif", "else" or ":endif" is found | 7067 // "where" is set when ":elseif", "else" or ":endif" is found |
7011 scope->se_u.se_if.is_if_label = instr->ga_len; | 7068 scope->se_u.se_if.is_if_label = instr->ga_len; |
7012 generate_JUMP(cctx, JUMP_IF_FALSE, 0); | 7069 generate_JUMP(cctx, JUMP_IF_FALSE, 0); |
7013 } | 7070 } |
7014 | 7071 |
7088 scope_T *scope = cctx->ctx_scope; | 7145 scope_T *scope = cctx->ctx_scope; |
7089 ifscope_T *ifscope; | 7146 ifscope_T *ifscope; |
7090 garray_T *instr = &cctx->ctx_instr; | 7147 garray_T *instr = &cctx->ctx_instr; |
7091 isn_T *isn; | 7148 isn_T *isn; |
7092 | 7149 |
7150 drop_cmdmod(cctx); | |
7093 if (scope == NULL || scope->se_type != IF_SCOPE) | 7151 if (scope == NULL || scope->se_type != IF_SCOPE) |
7094 { | 7152 { |
7095 emsg(_(e_endif_without_if)); | 7153 emsg(_(e_endif_without_if)); |
7096 return NULL; | 7154 return NULL; |
7097 } | 7155 } |
7158 char_u *p; | 7216 char_u *p; |
7159 char_u *wp; | 7217 char_u *wp; |
7160 int var_count = 0; | 7218 int var_count = 0; |
7161 int semicolon = FALSE; | 7219 int semicolon = FALSE; |
7162 size_t varlen; | 7220 size_t varlen; |
7163 garray_T *instr = &cctx->ctx_instr; | |
7164 garray_T *stack = &cctx->ctx_type_stack; | 7221 garray_T *stack = &cctx->ctx_type_stack; |
7165 scope_T *scope; | 7222 scope_T *scope; |
7166 lvar_T *loop_lvar; // loop iteration variable | 7223 lvar_T *loop_lvar; // loop iteration variable |
7167 lvar_T *var_lvar; // variable for "var" | 7224 lvar_T *var_lvar; // variable for "var" |
7168 type_T *vartype; | 7225 type_T *vartype; |
7228 else if (vartype->tt_member->tt_type == VAR_LIST | 7285 else if (vartype->tt_member->tt_type == VAR_LIST |
7229 && vartype->tt_member->tt_member->tt_type != VAR_ANY) | 7286 && vartype->tt_member->tt_member->tt_type != VAR_ANY) |
7230 item_type = vartype->tt_member->tt_member; | 7287 item_type = vartype->tt_member->tt_member; |
7231 } | 7288 } |
7232 | 7289 |
7290 // CMDMOD_REV must come before the FOR instruction | |
7291 generate_undo_cmdmods(cctx); | |
7292 | |
7233 // "for_end" is set when ":endfor" is found | 7293 // "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); |
7235 generate_FOR(cctx, loop_lvar->lv_idx); | 7295 generate_FOR(cctx, loop_lvar->lv_idx); |
7236 | 7296 |
7237 arg = arg_start; | 7297 arg = arg_start; |
7238 if (var_count > 1) | 7298 if (var_count > 1) |
7239 { | 7299 { |
7330 { | 7390 { |
7331 garray_T *instr = &cctx->ctx_instr; | 7391 garray_T *instr = &cctx->ctx_instr; |
7332 scope_T *scope = cctx->ctx_scope; | 7392 scope_T *scope = cctx->ctx_scope; |
7333 forscope_T *forscope; | 7393 forscope_T *forscope; |
7334 isn_T *isn; | 7394 isn_T *isn; |
7395 | |
7396 drop_cmdmod(cctx); | |
7335 | 7397 |
7336 if (scope == NULL || scope->se_type != FOR_SCOPE) | 7398 if (scope == NULL || scope->se_type != FOR_SCOPE) |
7337 { | 7399 { |
7338 emsg(_(e_for)); | 7400 emsg(_(e_for)); |
7339 return NULL; | 7401 return NULL; |
7374 */ | 7436 */ |
7375 static char_u * | 7437 static char_u * |
7376 compile_while(char_u *arg, cctx_T *cctx) | 7438 compile_while(char_u *arg, cctx_T *cctx) |
7377 { | 7439 { |
7378 char_u *p = arg; | 7440 char_u *p = arg; |
7379 garray_T *instr = &cctx->ctx_instr; | |
7380 scope_T *scope; | 7441 scope_T *scope; |
7381 | 7442 |
7382 scope = new_scope(cctx, WHILE_SCOPE); | 7443 scope = new_scope(cctx, WHILE_SCOPE); |
7383 if (scope == NULL) | 7444 if (scope == NULL) |
7384 return NULL; | 7445 return NULL; |
7385 | 7446 |
7386 // "endwhile" jumps back here, one before when profiling | 7447 // "endwhile" jumps back here, one before when profiling or using cmdmods |
7387 scope->se_u.se_while.ws_top_label = instr->ga_len; | 7448 scope->se_u.se_while.ws_top_label = current_instr_idx(cctx); |
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 | |
7393 | 7449 |
7394 // compile "expr" | 7450 // compile "expr" |
7395 if (compile_expr0(&p, cctx) == FAIL) | 7451 if (compile_expr0(&p, cctx) == FAIL) |
7396 return NULL; | 7452 return NULL; |
7397 if (!ends_excmd2(arg, skipwhite(p))) | 7453 if (!ends_excmd2(arg, skipwhite(p))) |
7401 } | 7457 } |
7402 | 7458 |
7403 if (bool_on_stack(cctx) == FAIL) | 7459 if (bool_on_stack(cctx) == FAIL) |
7404 return FAIL; | 7460 return FAIL; |
7405 | 7461 |
7462 // CMDMOD_REV must come before the jump | |
7463 generate_undo_cmdmods(cctx); | |
7464 | |
7406 // "while_end" is set when ":endwhile" is found | 7465 // "while_end" is set when ":endwhile" is found |
7407 if (compile_jump_to_end(&scope->se_u.se_while.ws_end_label, | 7466 if (compile_jump_to_end(&scope->se_u.se_while.ws_end_label, |
7408 JUMP_IF_FALSE, cctx) == FAIL) | 7467 JUMP_IF_FALSE, cctx) == FAIL) |
7409 return FAIL; | 7468 return FAIL; |
7410 | 7469 |
7418 compile_endwhile(char_u *arg, cctx_T *cctx) | 7477 compile_endwhile(char_u *arg, cctx_T *cctx) |
7419 { | 7478 { |
7420 scope_T *scope = cctx->ctx_scope; | 7479 scope_T *scope = cctx->ctx_scope; |
7421 garray_T *instr = &cctx->ctx_instr; | 7480 garray_T *instr = &cctx->ctx_instr; |
7422 | 7481 |
7482 drop_cmdmod(cctx); | |
7423 if (scope == NULL || scope->se_type != WHILE_SCOPE) | 7483 if (scope == NULL || scope->se_type != WHILE_SCOPE) |
7424 { | 7484 { |
7425 emsg(_(e_while)); | 7485 emsg(_(e_while)); |
7426 return NULL; | 7486 return NULL; |
7427 } | 7487 } |